mirror of
https://github.com/owncloud/android-library.git
synced 2025-06-07 16:06:08 +00:00
Compare commits
1009 Commits
oc-android
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
71224c30d1 | ||
|
c82d75b1d8 | ||
|
530999848f | ||
|
b4625d017c | ||
|
2c698cc2a4 | ||
|
bf183fe04d | ||
|
1f8de383b6 | ||
|
8eb435a1c2 | ||
|
052566d205 | ||
|
063c3fa9e9 | ||
|
fa143804d2 | ||
|
5b64876e2c | ||
|
d81ca97f14 | ||
|
5607f76a1d | ||
|
b4137502d2 | ||
|
261075a8ad | ||
|
c2874357f0 | ||
|
e6937b4210 | ||
|
89dd13cec4 | ||
|
ab3a594e5c | ||
|
cbbe044212 | ||
|
17aa1ea7bd | ||
|
cbf6365e93 | ||
|
15bccd3f80 | ||
|
8cee0a37a7 | ||
|
393ec6e07e | ||
|
f35daacdf1 | ||
|
2ac5cf0657 | ||
|
173b12eeca | ||
|
602ab41fcc | ||
|
3a27baad3a | ||
|
14baadd7ea | ||
|
fa65c1a84d | ||
|
c429b575a9 | ||
|
f7d4d27ebb | ||
|
3681f1001a | ||
|
ee5130d3e6 | ||
|
1f93cfaf52 | ||
|
e6e30fc352 | ||
|
c53475da37 | ||
|
c2f32b2a82 | ||
|
6846e25fa3 | ||
|
0e9f9c6391 | ||
|
159dcd6d68 | ||
|
b660ee98fd | ||
|
ff90598a2d | ||
|
0017079a69 | ||
|
2458db1828 | ||
|
e6f3fd2e16 | ||
|
a395787e0b | ||
|
e9f291371d | ||
|
b65efc2b69 | ||
|
78665e8cb0 | ||
|
9c844aae7e | ||
|
2c18e7b679 | ||
|
cb73d537e4 | ||
|
8e4f243031 | ||
|
a65a82cae0 | ||
|
45f53656ba | ||
|
8a4fcb6550 | ||
|
31ccf56e97 | ||
|
0d7a49b3ff | ||
|
7bb94cf289 | ||
|
b1a3402656 | ||
|
f0dda9eb8b | ||
|
4f001550f9 | ||
|
e30246c486 | ||
|
602b7e7548 | ||
|
f8da84d0ad | ||
|
763005b051 | ||
|
d6f969bd91 | ||
|
4200ed324c | ||
|
7ee2c5eb15 | ||
|
b5cd4a1df2 | ||
|
8a8a931e66 | ||
|
148a97cd32 | ||
|
00948ffd73 | ||
|
118646290d | ||
|
defa4d1469 | ||
|
3545686a31 | ||
|
b710070426 | ||
|
a0750c613a | ||
|
9df62a51f3 | ||
|
fa7bfdd597 | ||
|
cb076aa6b9 | ||
|
7ceef58382 | ||
|
a877612fca | ||
|
359e591275 | ||
|
f9bc792ded | ||
|
9adadbddcc | ||
|
1ecb8020b1 | ||
|
b7d3cc2687 | ||
|
2b27b9657c | ||
|
959cb7b015 | ||
|
b59a4e6947 | ||
|
17821e5760 | ||
|
2b79175b5a | ||
|
b25fbc4604 | ||
|
79173af930 | ||
|
c36eedd481 | ||
|
ba37bce0e1 | ||
|
dc7022c531 | ||
|
1f499fb67d | ||
|
12040a1261 | ||
|
7e56412748 | ||
|
71e9bbd2da | ||
|
cbbeaab251 | ||
|
340e114a91 | ||
|
c6584d69fd | ||
|
4c39990edb | ||
|
34cc4c87b1 | ||
|
5449eb7480 | ||
|
33d6141613 | ||
|
f7645c1648 | ||
|
6f33dca672 | ||
|
6fb5350dea | ||
|
24037a7a3e | ||
|
5f01b32b12 | ||
|
f849cd76d4 | ||
|
7c825b2dc3 | ||
|
b3cccfa007 | ||
|
5beb30e07a | ||
|
d736c7cdbf | ||
|
d99444e279 | ||
|
86f16c3460 | ||
|
62dbdb1021 | ||
|
f706595a9a | ||
|
03303ac4f2 | ||
|
039245742c | ||
|
3a996ef583 | ||
|
ec71fa6c23 | ||
|
56248af6ec | ||
|
389c0a0fc6 | ||
|
405da9a729 | ||
|
a02844d2c7 | ||
|
53c99a21c2 | ||
|
5e478036ae | ||
|
cd94e39b7f | ||
|
b0798194be | ||
|
2b5f80bdb9 | ||
|
32891e2cf5 | ||
|
d164b34fe8 | ||
|
c2f7426a3e | ||
|
1e9d8cef42 | ||
|
b083debac9 | ||
|
376d52ac5b | ||
|
e769684920 | ||
|
70bf35f683 | ||
|
1afa124b7d | ||
|
f19b2355b4 | ||
|
c966bf14d0 | ||
|
2c225d8e66 | ||
|
f8a829d6b7 | ||
|
0ea053d612 | ||
|
0be462c9aa | ||
|
32ef5d2125 | ||
|
6282fbd419 | ||
|
6d235fe74a | ||
|
9ab7c139e1 | ||
|
24b850d20c | ||
|
5be4eca0f7 | ||
|
2dc6f30a5e | ||
|
f712a384cd | ||
|
1f9bff1df0 | ||
|
1412117dcd | ||
|
a054adc688 | ||
|
809e641b1a | ||
|
fd8caa42cb | ||
|
45a9a190f8 | ||
|
2931a5494a | ||
|
35fb64c1bd | ||
|
544fc6efd4 | ||
|
16c31988e0 | ||
|
7fe41ce51c | ||
|
aaafdb5ea2 | ||
|
6e503c2cf7 | ||
|
4de236b3c7 | ||
|
f6eb631fbf | ||
|
be758eb5ea | ||
|
d16ab2e643 | ||
|
105830e4f0 | ||
|
09375ce053 | ||
|
fc4ae27bbe | ||
|
344c1e1136 | ||
|
79e4287223 | ||
|
0313c1e103 | ||
|
06e361afaa | ||
|
217216e43f | ||
|
060a988978 | ||
|
e0b81bd11a | ||
|
97a613f168 | ||
|
d8ac22d57e | ||
|
7c77c267f9 | ||
|
2a4195c966 | ||
|
d9dce81ce7 | ||
|
4f3e167efa | ||
|
fc8440cc01 | ||
|
5ca99a0e69 | ||
|
ce761aaec2 | ||
|
c59d1540c7 | ||
|
5582097ca1 | ||
|
e27a968ddb | ||
|
cfd69987e9 | ||
|
b2f6d7f3b1 | ||
|
7ccb86c153 | ||
|
e878ad3931 | ||
|
8d09a5c242 | ||
|
ddb15a33f1 | ||
|
c2c351c912 | ||
|
ca206173df | ||
|
7e4b43e7cb | ||
|
0e82f983b5 | ||
|
0d94058db9 | ||
|
2f952a3a09 | ||
|
39b1fce7f6 | ||
|
351efb7bf2 | ||
|
2edfc17653 | ||
|
e0efa7b3ab | ||
|
5ab6514164 | ||
|
194894637e | ||
|
c662383182 | ||
|
f8c7c12f5b | ||
|
61508c85d1 | ||
|
5619c1daf8 | ||
|
85a3918ff1 | ||
|
7e507abf32 | ||
|
a7e9138593 | ||
|
0ae5cf5ca2 | ||
|
063e7366e5 | ||
|
5cec7b43e2 | ||
|
ff0cde35fe | ||
|
a9a6ca6d4e | ||
|
57882e793e | ||
|
7365c0b126 | ||
|
5bebbe2d3d | ||
|
3aa9e72a22 | ||
|
cc53ed6f68 | ||
|
377572e87a | ||
|
9fc5ddbc39 | ||
|
b649ee0ad6 | ||
|
e6e763882c | ||
|
27e5d367af | ||
|
2b299da415 | ||
|
2c0745f20a | ||
|
1a4b98d232 | ||
|
aef967bf25 | ||
|
5ce81e3b17 | ||
|
4fa986e053 | ||
|
5e8f0b8286 | ||
|
050b98f78b | ||
|
487c4f682a | ||
|
f75f64ab13 | ||
|
dd088d7222 | ||
|
05705722ef | ||
|
36cf45c377 | ||
|
eea5240bd0 | ||
|
20efe90e2d | ||
|
83048b61d8 | ||
|
8a3ba16f96 | ||
|
e6b3df103e | ||
|
37bae10618 | ||
|
10e44627e1 | ||
|
1baeebe5ee | ||
|
fa7d83edb3 | ||
|
46dcb4434d | ||
|
3c46c95ac1 | ||
|
f14abb0bcd | ||
|
0895732ed4 | ||
|
91bd322b68 | ||
|
7baf3e43a5 | ||
|
6285c6c70a | ||
|
b40a394ab1 | ||
|
037a2b30df | ||
|
45fb12df0e | ||
|
27b18c33a9 | ||
|
c24ffcfaa4 | ||
|
fbf0fab800 | ||
|
5852921764 | ||
|
7b5c070175 | ||
|
650f348c35 | ||
|
20070775d7 | ||
|
593da77a04 | ||
|
3025ddce23 | ||
|
ac2f837f9e | ||
|
22719c8f40 | ||
|
12b009e378 | ||
|
5c2be25d66 | ||
|
99e636e8f6 | ||
|
022c486603 | ||
|
b491641eff | ||
|
86bfc3383c | ||
|
58ac89cb30 | ||
|
cfe5793d0d | ||
|
c02a4dea91 | ||
|
e9d23d9cd2 | ||
|
93497ca225 | ||
|
abdbdbe4cc | ||
|
351682cc7f | ||
|
634c4a0f93 | ||
|
307bf93a40 | ||
|
d0e50c4fca | ||
|
bf6f93ff1c | ||
|
2c18ae4ebb | ||
|
594ed2ee1b | ||
|
ccaf5a8e0e | ||
|
8c4a2708c2 | ||
|
3f8ddd0ba9 | ||
|
13344ae622 | ||
|
845b61ea4d | ||
|
257f616b0f | ||
|
7dc81fb74c | ||
|
1287035311 | ||
|
5ca9d5e330 | ||
|
7924561a62 | ||
|
aa65410535 | ||
|
88bb79c5ea | ||
|
87aa7137e7 | ||
|
4df880357c | ||
|
d1765eefb3 | ||
|
bf0ff3ce11 | ||
|
286fd65aff | ||
|
a5574e1e45 | ||
|
0daa19cf90 | ||
|
663e067d08 | ||
|
a60c4909f4 | ||
|
b96a3e9be3 | ||
|
46495e0df2 | ||
|
87a05491ab | ||
|
f248448e08 | ||
|
3a79a86cd7 | ||
|
1194b9a412 | ||
|
2a23a1c773 | ||
|
9ad0e8f9bc | ||
|
61ee5aab91 | ||
|
950d3a50da | ||
|
8eaa98af30 | ||
|
6ea9f9996d | ||
|
f9ea701e2f | ||
|
51dacd0bb0 | ||
|
033e6822a2 | ||
|
f8904dca9d | ||
|
654efea7e5 | ||
|
c3189b7b46 | ||
|
c54f1c98f6 | ||
|
cba63c060f | ||
|
d0a710e31b | ||
|
91c7900d5c | ||
|
b207fd1c67 | ||
|
dac7bcc0e7 | ||
|
270e127940 | ||
|
84240ef78b | ||
|
9d4b88cad7 | ||
|
cbfd977350 | ||
|
257cbcff03 | ||
|
cc91ad9dde | ||
|
9ffe758dca | ||
|
e821bee594 | ||
|
4676eb48d7 | ||
|
38d4dd604e | ||
|
0a31a1d3ef | ||
|
97df0b655d | ||
|
ec4c0b2a7f | ||
|
e438ded413 | ||
|
94065ae449 | ||
|
37250fc55c | ||
|
f35f41688a | ||
|
3b138e7c01 | ||
|
67699ffcc0 | ||
|
8e38c26374 | ||
|
e792cf6c70 | ||
|
fbbb1c9adb | ||
|
0e325cabd6 | ||
|
9ae720502f | ||
|
0d09540f9b | ||
|
44a13f9878 | ||
|
df8e10fac3 | ||
|
9208af89fe | ||
|
d683da3602 | ||
|
44967be4e3 | ||
|
f289746c2f | ||
|
0fc3f7668d | ||
|
18b271665d | ||
|
c3ffe3d916 | ||
|
65a4aaac28 | ||
|
7ccda7d249 | ||
|
114a7221f3 | ||
|
6253f1e198 | ||
|
cd8878352d | ||
|
2a4f8175da | ||
|
9bef933428 | ||
|
102747c392 | ||
|
ceee204399 | ||
|
0eeb48f7de | ||
|
a18528effc | ||
|
20c7c88a1f | ||
|
3441f4048c | ||
|
af579a3fd0 | ||
|
68b88775e6 | ||
|
ce9fc3f189 | ||
|
257494a9dc | ||
|
6ec1bd2bb2 | ||
|
76c55c9a9c | ||
|
db478efedc | ||
|
29de5aba34 | ||
|
34bf83df30 | ||
|
90b6ff52f2 | ||
|
26def4fbe2 | ||
|
7402c89a18 | ||
|
fe425c8083 | ||
|
b86638412e | ||
|
1c08a942d1 | ||
|
6bdb2badca | ||
|
9f1c20b418 | ||
|
4f72f93f8b | ||
|
8a04231473 | ||
|
61937825b8 | ||
|
2b64b83b89 | ||
|
5e86e9f0e4 | ||
|
460f85f2e1 | ||
|
943c5ecb97 | ||
|
781f958c93 | ||
|
db97ed7fb3 | ||
|
197c86ec64 | ||
|
c0d2e20bb1 | ||
|
aa665a7295 | ||
|
dc39475760 | ||
|
b7fd663d77 | ||
|
dc215dc80e | ||
|
a564f18f16 | ||
|
775fdb080c | ||
|
e28335ae4e | ||
|
4b6946a195 | ||
|
5d21f01f38 | ||
|
4d22971de0 | ||
|
411364b378 | ||
|
a55498c64b | ||
|
8da24c5076 | ||
|
154507773d | ||
|
925f1e79aa | ||
|
6d5d90d58c | ||
|
f184347235 | ||
|
a7a269a9e4 | ||
|
f09162b213 | ||
|
31d3bfbde9 | ||
|
f89cfd91a9 | ||
|
69497645ec | ||
|
c5891b0a14 | ||
|
b287cb9148 | ||
|
93026e8180 | ||
|
c5cf0a8c0f | ||
|
aebd7288cd | ||
|
7f2d94bc78 | ||
|
c5fd59c825 | ||
|
f93887732c | ||
|
ffe7d9d8e3 | ||
|
390ef9fbd7 | ||
|
6cb2642ba1 | ||
|
ceab9378ab | ||
|
137b417580 | ||
|
23a38a7fe6 | ||
|
cdd16046a8 | ||
|
070ac89fa9 | ||
|
d28927712c | ||
|
7536ba8f3c | ||
|
d6ea5800eb | ||
|
59229aa6ab | ||
|
ac21650da7 | ||
|
d997c84a80 | ||
|
6f74907af6 | ||
|
8a88fbd938 | ||
|
bbac8d0278 | ||
|
337c57da1a | ||
|
85782e4818 | ||
|
e5e226dee0 | ||
|
76a808cb0d | ||
|
7710db7612 | ||
|
d157c107c8 | ||
|
5ef9243a53 | ||
|
f77de321b7 | ||
|
d6eb080389 | ||
|
1cee1a7a65 | ||
|
8708af1cfd | ||
|
e2f858238b | ||
|
0e39f949e5 | ||
|
837aa39b23 | ||
|
fc8c256463 | ||
|
8228374d71 | ||
|
fba6436b56 | ||
|
9e9877f2bf | ||
|
42b923c3b8 | ||
|
679b91df7c | ||
|
b10e74afec | ||
|
7b29f8f09e | ||
|
48ee263951 | ||
|
0f10c725c7 | ||
|
7f7fa0b74e | ||
|
8f62925312 | ||
|
d957d48279 | ||
|
7b32f7bbe4 | ||
|
5371c682d4 | ||
|
f20d2554bf | ||
|
64bfd6a7f7 | ||
|
54d03cdfd5 | ||
|
5ecd3c9311 | ||
|
9e46b83901 | ||
|
18b0abb01a | ||
|
bec5643714 | ||
|
cf06126ce1 | ||
|
0028d63292 | ||
|
c6e88b127c | ||
|
d2ee1294f2 | ||
|
7e6b63db1f | ||
|
1536a455a6 | ||
|
4f07187fa2 | ||
|
3829a1ca32 | ||
|
ec5c9fc4aa | ||
|
ad533c8307 | ||
|
921983c16e | ||
|
99ced8bf41 | ||
|
c5e64bd7ac | ||
|
96ea90e349 | ||
|
18f4775db6 | ||
|
9ec6e74d6e | ||
|
ffcf603b8b | ||
|
9331f7e8b3 | ||
|
48f720011d | ||
|
1945d311d7 | ||
|
1d767f1e13 | ||
|
6803a347bf | ||
|
e6e3af2082 | ||
|
8d5524da75 | ||
|
f7a54a8218 | ||
|
e343a720f7 | ||
|
8f9bed2d9f | ||
|
0f1f232c16 | ||
|
4a9eb24d69 | ||
|
b77cecedab | ||
|
1b4ce388b3 | ||
|
4153ee4e35 | ||
|
261be8bf85 | ||
|
b7033e53bf | ||
|
fb8dc389a4 | ||
|
b04a314478 | ||
|
c7f9c9d201 | ||
|
5b3c21ba82 | ||
|
e2be78c8f8 | ||
|
015011b6e0 | ||
|
ffbd4e9128 | ||
|
11a2ec1d3e | ||
|
9beb25d019 | ||
|
a47d6fd882 | ||
|
4e03070641 | ||
|
b72353f025 | ||
|
2aec82b5e2 | ||
|
6df51cc323 | ||
|
4cd317d929 | ||
|
03e30ae698 | ||
|
52463707c7 | ||
|
20e4317bd9 | ||
|
8ba4fa5960 | ||
|
495e3321e2 | ||
|
234add959a | ||
|
bcc5a06335 | ||
|
285306e1bc | ||
|
073a91c580 | ||
|
c0a11c5d0b | ||
|
c5ac449fed | ||
|
2778762217 | ||
|
a63dd63b39 | ||
|
d76ecc3bc6 | ||
|
3204d5e60e | ||
|
a1d4c781ae | ||
|
fadc578f07 | ||
|
9a806469c5 | ||
|
169836683e | ||
|
094e698e95 | ||
|
66c8b906fa | ||
|
90210949b5 | ||
|
e5939e8b70 | ||
|
3262ad4c48 | ||
|
fdeb504d87 | ||
|
3d9d943825 | ||
|
a708b794c0 | ||
|
1aeacd62b8 | ||
|
9710cf991d | ||
|
da08f04fa1 | ||
|
f104d6842e | ||
|
c50e199b76 | ||
|
c05a11a7b4 | ||
|
8fa6cc648b | ||
|
27ff4dfd23 | ||
|
28d93a6790 | ||
|
f32a3af572 | ||
|
9ea10e2f96 | ||
|
2e460d9cf7 | ||
|
9230937a44 | ||
|
d73ee43e3d | ||
|
9aff678fa4 | ||
|
cc38dcd9a6 | ||
|
32ecf729c5 | ||
|
31868dca00 | ||
|
66490ff2b0 | ||
|
ebf1a08508 | ||
|
ea34453f0a | ||
|
5981dfab96 | ||
|
e28ffbac7c | ||
|
3d7a04a12d | ||
|
5bf99df58a | ||
|
10a7bdbc4d | ||
|
fa4db38a41 | ||
|
b084ca7c03 | ||
|
d32f35147f | ||
|
035c5fdf65 | ||
|
07e00a720d | ||
|
68063fa9fa | ||
|
fdde542f15 | ||
|
e2f1637c80 | ||
|
eb177543fc | ||
|
2913176f81 | ||
|
c67fa8b7b1 | ||
|
87d244e473 | ||
|
fca43e1fb8 | ||
|
d0eb8293fd | ||
|
fc8c54f142 | ||
|
bb45bc9dbb | ||
|
2dcfcc2545 | ||
|
2b62387e80 | ||
|
b66a82e1de | ||
|
159bc0789e | ||
|
36df3d9287 | ||
|
90cef9fcf6 | ||
|
07cdef9afa | ||
|
8db0b820ff | ||
|
14e99551a9 | ||
|
c65d8fa21f | ||
|
0f15c38fc2 | ||
|
3b552b7ea6 | ||
|
f36aa8d826 | ||
|
c7d0f83683 | ||
|
176de93abc | ||
|
91c6183090 | ||
|
c17c60a0a3 | ||
|
b2b32c048b | ||
|
c8d93400e0 | ||
|
253e69f415 | ||
|
06bc2ddf6f | ||
|
80be123cec | ||
|
667958c82f | ||
|
4391f201bd | ||
|
ca7a0c8ea8 | ||
|
95dde86eda | ||
|
1087f0fd46 | ||
|
daf49d289b | ||
|
1632b237ff | ||
|
59048e8e7a | ||
|
4849be5daa | ||
|
688a978b1e | ||
|
8fa7439492 | ||
|
4f57487f70 | ||
|
6bd64197b4 | ||
|
64f5f08ded | ||
|
a28f58f0fe | ||
|
0614e7a985 | ||
|
1114c38ff7 | ||
|
cbb15c3626 | ||
|
c2d8e68ecc | ||
|
af6b18e042 | ||
|
261e7a762d | ||
|
418f5af783 | ||
|
92d57eadbe | ||
|
4e78a5f3e8 | ||
|
91ffc87014 | ||
|
3c6f476176 | ||
|
0b3c8a7bf8 | ||
|
d7b190d06b | ||
|
54283c0ba9 | ||
|
f11cab39bb | ||
|
1b727ae71d | ||
|
24111a95d5 | ||
|
f16ac0de97 | ||
|
4660322eb0 | ||
|
b5efb1d664 | ||
|
a7ad9d4152 | ||
|
87841a55d0 | ||
|
e233fe0587 | ||
|
1f220013f1 | ||
|
510ec1798e | ||
|
ec01a21137 | ||
|
e4d893d150 | ||
|
79283cd6ed | ||
|
446ac7c489 | ||
|
6b61e66318 | ||
|
4323c3a89b | ||
|
4b0510d791 | ||
|
b22e389f1f | ||
|
04819cc714 | ||
|
239e920d56 | ||
|
87a8b418de | ||
|
59ed7e42ec | ||
|
249e25768b | ||
|
78e4d167d2 | ||
|
ebd08d6a2d | ||
|
f249d2dc11 | ||
|
42d530eb95 | ||
|
f8279ab70f | ||
|
490fbde496 | ||
|
2f64bc0b89 | ||
|
82a207cb62 | ||
|
fdfd30d8ba | ||
|
c99e2fb7b3 | ||
|
40690df5fa | ||
|
6d05856fb7 | ||
|
5f579fbb24 | ||
|
940f0b9bb0 | ||
|
6560aa987d | ||
|
dffd5bed9f | ||
|
7954bdbcdd | ||
|
1aae034b70 | ||
|
badc15b741 | ||
|
8a8906154c | ||
|
8c6a325e06 | ||
|
c9337dc251 | ||
|
982dbb2a93 | ||
|
0eb071b762 | ||
|
9fb69ce38d | ||
|
56e003139f | ||
|
137e489008 | ||
|
7dc611cd14 | ||
|
dbeb4b33de | ||
|
9502498245 | ||
|
94f3c377e4 | ||
|
6eba501c4b | ||
|
c85fe7549c | ||
|
b523062740 | ||
|
3b551ea439 | ||
|
f09c851210 | ||
|
e1b09a5ff5 | ||
|
d367348217 | ||
|
5ede3c037b | ||
|
f3e8374429 | ||
|
2c5ceeb555 | ||
|
7a50007ba3 | ||
|
e857853a58 | ||
|
e0a6c68c66 | ||
|
106b4fd9e5 | ||
|
69c7a24ebb | ||
|
f6471b66ca | ||
|
44372f211c | ||
|
4c9b63bded | ||
|
a69177bce4 | ||
|
b57212890c | ||
|
c3cecfc7b9 | ||
|
d49814c195 | ||
|
7238c5c04b | ||
|
1b72e2bad8 | ||
|
9526038503 | ||
|
2723924b5d | ||
|
2fdf724eab | ||
|
fabf8d55f0 | ||
|
14ec6cb1ee | ||
|
8841a0fcb7 | ||
|
33fd1037a3 | ||
|
096d037f94 | ||
|
c36a71caf2 | ||
|
d195b2ff89 | ||
|
5d3bba65e2 | ||
|
549cc32703 | ||
|
f2ccc62b88 | ||
|
ae7be42ec3 | ||
|
fef62aaf74 | ||
|
817e3c894b | ||
|
81f1804917 | ||
|
cc612f8712 | ||
|
800c075bd1 | ||
|
eaa645ab3c | ||
|
8a04392bfc | ||
|
f3eb0c4431 | ||
|
782361267d | ||
|
d30a5578f3 | ||
|
eb7cac064f | ||
|
e3db308825 | ||
|
50fda001ce | ||
|
7627f75aa9 | ||
|
b0fa6ae1d0 | ||
|
9944f30d3f | ||
|
d944c500dd | ||
|
fd6628e38f | ||
|
c5a1df417f | ||
|
096d3b9165 | ||
|
7d0c79876c | ||
|
c34c8ead1a | ||
|
e0e6d3da49 | ||
|
4968ca6e62 | ||
|
7bc9a885b0 | ||
|
06ec99aead | ||
|
c33c7c551d | ||
|
bd5dc20842 | ||
|
f1c6726d30 | ||
|
78613ed4bc | ||
|
c8a0451e06 | ||
|
1d80067f85 | ||
|
ed073d6db0 | ||
|
762ae7eb62 | ||
|
e6bdfab11a | ||
|
0b9a79491f | ||
|
b99723205b | ||
|
0d0b711556 | ||
|
6e4ded84d9 | ||
|
75905b5f74 | ||
|
153c0a10ff | ||
|
366459699e | ||
|
266dc37da2 | ||
|
297e8d1848 | ||
|
2d54ece72e | ||
|
f218a60611 | ||
|
d013d93ac9 | ||
|
c2bdea1c91 | ||
|
45e4b98510 | ||
|
804b31da39 | ||
|
e261b1df2f | ||
|
f110992489 | ||
|
8540eed348 | ||
|
b1f2c0cbef | ||
|
9caa79becc | ||
|
60c1be2c0c | ||
|
82fce2944b | ||
|
ff17ab6837 | ||
|
2041fb1962 | ||
|
e78b96348b | ||
|
019383378d | ||
|
cb08fe32a4 | ||
|
6a375dcb93 | ||
|
73b4ffe0b9 | ||
|
af5228a425 | ||
|
0387ceb1d4 | ||
|
b183e3f08a | ||
|
e4e8a5a5e3 | ||
|
524dc587db | ||
|
c676f694d6 | ||
|
a0478826b4 | ||
|
1631b9b55a | ||
|
f5795d1c03 | ||
|
7fe2c02fc1 | ||
|
bcc8a78bcf | ||
|
08777c0265 | ||
|
226e332460 | ||
|
6d1e2d0434 | ||
|
7d534c90ab | ||
|
1a7e6a475d | ||
|
3b7950ae59 | ||
|
944a1186d9 | ||
|
82b7bef113 | ||
|
a730980c34 | ||
|
01d4592de0 | ||
|
25bba85ee1 | ||
|
bd82537045 | ||
|
ac869690fc | ||
|
e7c5391ac5 | ||
|
dc9a37db28 | ||
|
1cf44a9fff | ||
|
dfab453e6b | ||
|
4f0cd82a7d | ||
|
81aa417cbf | ||
|
d67a41d5cc | ||
|
11938a1fcc | ||
|
b9ba124541 | ||
|
275a0f0f10 | ||
|
2b333b8ff0 | ||
|
81cea2788d | ||
|
81d3d832e9 | ||
|
ce86ebac8a | ||
|
74c9430382 | ||
|
81120ac7ad | ||
|
0f14e89d7c | ||
|
5d0362dc4d | ||
|
dae28aee48 | ||
|
63c917c979 | ||
|
75ce8e186f | ||
|
2ef51dd5f6 | ||
|
2ba891df15 | ||
|
77c7c5195b | ||
|
68fae46240 | ||
|
7cb9189fce | ||
|
a4678557d8 | ||
|
ca2eff6647 | ||
|
d593474113 | ||
|
ff6adc4cf9 | ||
|
3125c8047c | ||
|
1249e205d6 | ||
|
e354a716cb | ||
|
cd3d20db07 | ||
|
9ab1b40682 | ||
|
d4908e4172 | ||
|
7bed924ff8 | ||
|
8e357d7811 | ||
|
813900d600 | ||
|
38c84ed377 | ||
|
fdbe23923e | ||
|
f3566d667e | ||
|
a21614fd5a | ||
|
ec08bfbd7a | ||
|
12429b7ab9 | ||
|
ad0adaaf5c | ||
|
2888b2d55e | ||
|
bf225b275f | ||
|
2ccd681d2f | ||
|
9a2fb4cd0a | ||
|
35ea9dfd20 | ||
|
91f509a119 | ||
|
b1ef9ec44b | ||
|
1ff922af6a | ||
|
3bd29307a5 | ||
|
41fed5d9f0 | ||
|
e6835b1518 | ||
|
d0d26b7a3b | ||
|
872c82b79a | ||
|
bc32ddda0f | ||
|
f3a080e9a8 | ||
|
bdd6a1be0c | ||
|
d79bdb7893 | ||
|
ede34920c2 | ||
|
e6640d395b | ||
|
a2a1b0204a | ||
|
0abc3a6b29 | ||
|
0e7fd8ee8f | ||
|
7c7cad7e99 | ||
|
9fe3e11c52 | ||
|
c18329efd6 | ||
|
a61d287bf1 | ||
|
848d9db763 | ||
|
1b34e9bd49 | ||
|
98eda17cba | ||
|
826abcfae4 | ||
|
132e173078 | ||
|
e0a6926608 | ||
|
2109ab98a1 | ||
|
2dde542356 | ||
|
19cea29faf | ||
|
2418abf85e | ||
|
40af731eb0 | ||
|
c560f4fb4b | ||
|
f389207713 | ||
|
f3a2c05efa | ||
|
2d35b00273 | ||
|
3072507972 | ||
|
2289358775 | ||
|
75821de7a3 | ||
|
12d04bb63c | ||
|
e466bac6b1 | ||
|
dd2d3b8996 | ||
|
c7ade613c6 | ||
|
3ea5ad32f6 | ||
|
ae4b463478 | ||
|
02f35d5f5e | ||
|
a4386bb4fe | ||
|
c04f6e3463 | ||
|
7f98b3804c | ||
|
99334c2e45 | ||
|
aa717b3e7b | ||
|
ae8e3ca904 | ||
|
95fdf75d38 | ||
|
c6f8430876 | ||
|
a06af810d0 | ||
|
0dc859c071 | ||
|
a1b1c2f9ed | ||
|
fe947194c5 | ||
|
2df2cde466 | ||
|
f6ee1a15cb | ||
|
e4b57e8063 | ||
|
312b21cb1e | ||
|
c75efa81ba | ||
|
0a4c3773b2 | ||
|
b074c34cbc | ||
|
4b2c32511b | ||
|
cfd43dc058 | ||
|
5d99c9db32 | ||
|
6842cd6968 | ||
|
7b1a5cdee9 | ||
|
33390ff09c | ||
|
137a892d2e | ||
|
bcb6c2c106 | ||
|
e7b2dde940 | ||
|
5493227dd6 | ||
|
bac38d13ef | ||
|
defbfc8d0e | ||
|
c9a06fc5e4 | ||
|
a034338290 | ||
|
beb9be05de | ||
|
8f5ac456ab | ||
|
6d0773cf34 | ||
|
8ae4dd137d | ||
|
e80b50d4de | ||
|
8f3aeb30ed | ||
|
ee726119cb | ||
|
dda1429581 | ||
|
188ce07155 | ||
|
0dce40b160 | ||
|
11363adedb | ||
|
0e5cf18d8c | ||
|
9fe7c995dd | ||
|
02e3a90df3 | ||
|
e6f9eb43d9 | ||
|
096b46ca3c | ||
|
24041deb6d | ||
|
7d49e20258 | ||
|
1f69348cc6 | ||
|
f42a9f8c61 | ||
|
825ef70cef | ||
|
ef49035784 |
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
5
.editorconfig
Normal file
5
.editorconfig
Normal file
@ -0,0 +1,5 @@
|
||||
[*]
|
||||
max_line_length = 150
|
||||
|
||||
[*.{kt, kts}]
|
||||
disabled_rules=no-consecutive-blank-lines,no-wildcard-imports,max-line-length,no-blank-line-before-rbrace,final-newline,indent,no-multi-spaces,comment-spacing,parameter-list-wrapping
|
17
.github/dependabot.yml
vendored
Normal file
17
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gradle"
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
10
.github/workflows/gradle-wrapper-validation.yml
vendored
Normal file
10
.github/workflows/gradle-wrapper-validation.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
name: "Validate Gradle Wrapper"
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
validation:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
15
.gitignore
vendored
15
.gitignore
vendored
@ -2,6 +2,9 @@
|
||||
*.apk
|
||||
*.ap_
|
||||
|
||||
.idea/*
|
||||
!.idea/codeStyles/
|
||||
|
||||
# files for the dex VM
|
||||
*.dex
|
||||
|
||||
@ -9,25 +12,13 @@
|
||||
*.class
|
||||
|
||||
# generated files
|
||||
bin/
|
||||
build/
|
||||
gen/
|
||||
target/
|
||||
*.iml
|
||||
|
||||
# Local configuration files (sdk path, etc)
|
||||
.idea/
|
||||
.gradle/
|
||||
local.properties
|
||||
sample_client/local.properties
|
||||
tests/local.properties
|
||||
tests/test_cases/local.properties
|
||||
|
||||
# Mac .DS_Store files
|
||||
.DS_Store
|
||||
|
||||
# Proguard README
|
||||
proguard-project.txt
|
||||
sample_client/proguard-project.txt
|
||||
tests/proguard-project.txt
|
||||
tests/test_cases/proguard-project.txt
|
378
.idea/codeStyles/Project.xml
generated
Normal file
378
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,378 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<option name="RIGHT_MARGIN" value="150" />
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="FIELD_NAME_PREFIX" value="m" />
|
||||
<option name="STATIC_FIELD_NAME_PREFIX" value="s" />
|
||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99999" />
|
||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99999" />
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="android" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="javax" withSubpackages="true" static="false" />
|
||||
<package name="java" withSubpackages="true" static="false" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="true" />
|
||||
<emptyLine />
|
||||
</value>
|
||||
</option>
|
||||
</JavaCodeStyleSettings>
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<MarkdownNavigatorCodeStyleSettings>
|
||||
<option name="RIGHT_MARGIN" value="72" />
|
||||
</MarkdownNavigatorCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_ATTRIBUTE_WRAP" value="0" />
|
||||
<option name="XML_KEEP_BLANK_LINES" value="1" />
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="RIGHT_MARGIN" value="150" />
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<option name="WRAP_LONG_LINES" value="true" />
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<INITIALIZER_BLOCK>true</INITIALIZER_BLOCK>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<FINAL>true</FINAL>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<FIELD>true</FIELD>
|
||||
<PUBLIC>true</PUBLIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<FIELD>true</FIELD>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<INITIALIZER_BLOCK>true</INITIALIZER_BLOCK>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CONSTRUCTOR>true</CONSTRUCTOR>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<METHOD>true</METHOD>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<METHOD>true</METHOD>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<ENUM>true</ENUM>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<INTERFACE>true</INTERFACE>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<CLASS>true</CLASS>
|
||||
<STATIC>true</STATIC>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<CLASS>true</CLASS>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_width</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_height</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:layout_.*</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:width</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:height</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="FIELD_ANNOTATION_WRAP" value="0" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
33
.project
33
.project
@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>ownCloud Android Library</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
@ -1,4 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
48
.travis.yml
48
.travis.yml
@ -1,48 +0,0 @@
|
||||
sudo: false
|
||||
language: android
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
android:
|
||||
components:
|
||||
# first 'tools' updates SDK tools 'til last version ** in remote repository number 10 **
|
||||
- tools
|
||||
# second 'tools' updates SDK tools 'til last version ** in remote repository number 11 ** (current last one)
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-24.0.2
|
||||
- android-24
|
||||
- sys-img-armeabi-v7a-android-24
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
before_install:
|
||||
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
|
||||
-c 20M
|
||||
- emulator -avd test -no-window &
|
||||
- rm pom.xml
|
||||
- android update project -p .
|
||||
- chmod +x ./wait_for_emulator.sh
|
||||
- ./wait_for_emulator.sh
|
||||
script:
|
||||
- ant clean
|
||||
- ant debug
|
||||
- cd test_client/tests
|
||||
- ant acceptance-test
|
||||
- cd ../..
|
||||
- ./gradlew clean build
|
||||
env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
- secure: epTZ0zZGDbHL3o6vSC9uNkZsi5j5SA6O/tvQBH7QW/dluuzIJxIjfhNbZHDyBReYDleirLzUFQpdWAUdvulCMLs/qZdIzFGlYXZSpxEnvPYMGQcilwADdJcxLw8L+3+ET5hSexxhjrTGw427IljkqGUpqQTxaLwFdFu98lDWSbc=
|
||||
matrix:
|
||||
- ANDROID_TARGET=android-24 ANDROID_ABI=armeabi-v7a
|
||||
addons:
|
||||
coverity_scan:
|
||||
project:
|
||||
name: owncloud/android-library
|
||||
description: Build submitted via Travis CI
|
||||
notification_email: lukas@owncloud.com
|
||||
build_command_prepend: gradle clean
|
||||
build_command: gradle build
|
||||
branch_pattern: coverity_scan
|
@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- ownCloud Android Library is available under MIT license
|
||||
Copyright (C) 2016 ownCloud GmbH.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.owncloud.android.lib"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<!-- USE_CREDENTIALS, MANAGE_ACCOUNTS and AUTHENTICATE_ACCOUNTS are needed for API < 23.
|
||||
In API >= 23 the do not exist anymore -->
|
||||
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="8"
|
||||
android:targetSdkVersion="24" />
|
||||
|
||||
</manifest>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
ownCloud Android Library is available under MIT license
|
||||
|
||||
Copyright (C) 2016 ownCloud GmbH.
|
||||
Copyright (C) 2020 ownCloud GmbH.
|
||||
Copyright (C) 2012 Bartek Przybylski
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
@ -44,13 +44,11 @@ ownCloud Android Library is available under MIT license. See the file LICENSE.md
|
||||
|
||||
#### Third party libraries
|
||||
|
||||
ownCloud Android Library uses Apache JackRabbit, version 2.2.5. Copyright (C) 2004-2010 The Apache Software Foundation. Licensed under Apache License, Version 2.0.
|
||||
|
||||
Apache JackRabbit depends on Commons HTTPClient version 3.1 and SLF4j version 1.7.5; both included also. Copyright (C) 2004-2010 The Apache Software Foundation. Licensed under Apache License, Version 2.0.
|
||||
ownCloud Android Library uses OkHttp version 4.6.0, licensed under Apache License and version 2.0. Besides, it uses Dav4Android, licensed under Mozilla Public License, v. 2.0
|
||||
|
||||
|
||||
### Compatibility
|
||||
|
||||
ownCloud Android Library is valid for Android systems from version Android 2.2 (android:minSdkVersion="8" android:targetSdkVersion="19").
|
||||
ownCloud Android Library is valid for Android systems from version Android 6 (android:minSdkVersion="23" android:targetSdkVersion="33").
|
||||
|
||||
ownCloud Android library supports ownCloud server from version 4.5.
|
||||
|
69
build.gradle
69
build.gradle
@ -1,51 +1,34 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
ext {
|
||||
orgJetbrainsKotlin = '1.8.10'
|
||||
comSquareupMoshi = '1.14.0'
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.apache.jackrabbit:jackrabbit-webdav:2.12.4'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 24
|
||||
buildToolsVersion "24.0.2"
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
resources.srcDirs = ['src']
|
||||
aidl.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
assets.srcDirs = ['assets']
|
||||
}
|
||||
|
||||
// Move the tests to tests/java, tests/res, etc...
|
||||
instrumentTest.setRoot('tests')
|
||||
|
||||
// Move the build types to build-types/<type>
|
||||
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
|
||||
// This moves them out of them default location under src/<type>/... which would
|
||||
// conflict with src/ being used by the main source set.
|
||||
// Adding new build types or product flavors should be accompanied
|
||||
// by a similar customization.
|
||||
debug.setRoot('build-types/debug')
|
||||
release.setRoot('build-types/release')
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
classpath "org.jlleitschuh.gradle:ktlint-gradle:11.1.0"
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$orgJetbrainsKotlin"
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
apply plugin: "com.google.devtools.ksp"
|
||||
}
|
||||
|
92
build.xml
92
build.xml
@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="owncloud-android-library" default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
source.dir
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- if sdk.dir was not set from one of the property file, then
|
||||
get it from the ANDROID_HOME env var.
|
||||
This must be done before we load project.properties since
|
||||
the proguard config can use sdk.dir -->
|
||||
<property environment="env" />
|
||||
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||
<isset property="env.ANDROID_HOME" />
|
||||
</condition>
|
||||
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: 1 -->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
</project>
|
15
check_code_script.sh
Executable file
15
check_code_script.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
check_license_in_file() {
|
||||
if ! head -n 20 $FILE | grep -q "Permission is hereby granted, free of charge, to any person obtaining a copy"
|
||||
then
|
||||
echo "$FILE does not contain a current copyright header"
|
||||
fi
|
||||
}
|
||||
|
||||
for FILE in $(find owncloudComLibrary/src -name "*.java" -o -name "*.kt")
|
||||
do
|
||||
check_license_in_file
|
||||
done
|
||||
|
||||
./gradlew ktlintFormat
|
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="custom_rules">
|
||||
<target name="-post-compile">
|
||||
<echo>Copying jar file for binary distribution</echo>
|
||||
<copy file="${out.absolute.dir}/classes.jar" toFile="${out.absolute.dir}/${ant.project.name}.jar" />
|
||||
</target>
|
||||
</project>
|
3
gradle.properties
Normal file
3
gradle.properties
Normal file
@ -0,0 +1,3 @@
|
||||
android.enableJetifier=true
|
||||
android.useAndroidX=true
|
||||
org.gradle.jvmargs=-Xmx1536M
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,5 @@
|
||||
#Wed Aug 17 12:51:45 CEST 2016
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
|
||||
|
298
gradlew
vendored
298
gradlew
vendored
@ -1,79 +1,129 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@ -82,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
@ -90,75 +140,95 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
53
gradlew.bat
vendored
53
gradlew.bat
vendored
@ -1,3 +1,19 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@ -8,20 +24,23 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@ -35,7 +54,7 @@ goto fail
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
@ -45,34 +64,14 @@ echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
43
owncloudComLibrary/build.gradle
Normal file
43
owncloudComLibrary/build.gradle
Normal file
@ -0,0 +1,43 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'com.google.devtools.ksp'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
|
||||
dependencies {
|
||||
api 'com.squareup.okhttp3:okhttp:4.6.0'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$orgJetbrainsKotlin"
|
||||
api 'com.gitlab.ownclouders:dav4android:oc_support_2.1.5'
|
||||
api 'com.github.AppDevNext.Logcat:LogcatCore:2.2.2'
|
||||
|
||||
// Moshi
|
||||
implementation("com.squareup.moshi:moshi-kotlin:$comSquareupMoshi") {
|
||||
exclude module: "kotlin-reflect"
|
||||
}
|
||||
implementation 'org.apache.commons:commons-lang3:3.12.0'
|
||||
ksp "com.squareup.moshi:moshi-kotlin-codegen:$comSquareupMoshi"
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.10'
|
||||
debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.6.0'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 33
|
||||
}
|
||||
|
||||
lint {
|
||||
abortOnError false
|
||||
ignoreWarnings true
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
namespace 'com.owncloud.android.lib'
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.http
|
||||
|
||||
import com.facebook.stetho.okhttp3.StethoInterceptor
|
||||
|
||||
object DebugInterceptorFactory {
|
||||
fun getInterceptor() = StethoInterceptor()
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- ownCloud Android Library is available under MIT license
|
||||
Copyright (C) 2016 ownCloud GmbH.
|
||||
Copyright (C) 2023 ownCloud GmbH.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@ -23,8 +23,9 @@
|
||||
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<string name="app_name">Oc_framework-testTest</string>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
</manifest>
|
||||
|
||||
</resources>
|
@ -0,0 +1,221 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common
|
||||
|
||||
import android.accounts.AccountManager
|
||||
import android.accounts.AccountsException
|
||||
import android.content.Context
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation
|
||||
import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation
|
||||
import com.owncloud.android.lib.resources.status.RemoteServerInfo
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* ConnectionValidator
|
||||
*
|
||||
* @author Christian Schabesberger
|
||||
*/
|
||||
class ConnectionValidator(
|
||||
val context: Context,
|
||||
private val clearCookiesOnValidation: Boolean
|
||||
) {
|
||||
fun validate(baseClient: OwnCloudClient, singleSessionManager: SingleSessionManager, context: Context): Boolean {
|
||||
try {
|
||||
var validationRetryCount = 0
|
||||
val client = OwnCloudClient(baseClient.baseUri, null, false, singleSessionManager, context)
|
||||
if (clearCookiesOnValidation) {
|
||||
client.clearCookies()
|
||||
} else {
|
||||
client.cookiesForBaseUri = baseClient.cookiesForBaseUri
|
||||
}
|
||||
|
||||
client.account = baseClient.account
|
||||
client.credentials = baseClient.credentials
|
||||
while (validationRetryCount < VALIDATION_RETRY_COUNT) {
|
||||
Timber.d("validationRetryCount %d", validationRetryCount)
|
||||
var successCounter = 0
|
||||
var failCounter = 0
|
||||
|
||||
client.setFollowRedirects(true)
|
||||
if (isOwnCloudStatusOk(client)) {
|
||||
successCounter++
|
||||
} else {
|
||||
failCounter++
|
||||
}
|
||||
|
||||
// Skip the part where we try to check if we can access the parts where we have to be logged in... if we are not logged in
|
||||
if (baseClient.credentials !is OwnCloudAnonymousCredentials) {
|
||||
client.setFollowRedirects(false)
|
||||
val contentReply = canAccessRootFolder(client)
|
||||
if (contentReply.httpCode == HttpConstants.HTTP_OK) {
|
||||
if (contentReply.data == true) { //if data is true it means that the content reply was ok
|
||||
successCounter++
|
||||
} else {
|
||||
failCounter++
|
||||
}
|
||||
} else {
|
||||
failCounter++
|
||||
if (contentReply.httpCode == HttpConstants.HTTP_UNAUTHORIZED) {
|
||||
checkUnauthorizedAccess(client, singleSessionManager, contentReply.httpCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (successCounter >= failCounter) {
|
||||
baseClient.credentials = client.credentials
|
||||
baseClient.cookiesForBaseUri = client.cookiesForBaseUri
|
||||
return true
|
||||
}
|
||||
validationRetryCount++
|
||||
}
|
||||
Timber.d("Could not authenticate or get valid data from owncloud")
|
||||
} catch (e: Exception) {
|
||||
Timber.d(ExceptionUtils.getStackTrace(e))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isOwnCloudStatusOk(client: OwnCloudClient): Boolean {
|
||||
val reply = getOwnCloudStatus(client)
|
||||
// dont check status code. It currently relais on the broken redirect code of the owncloud client
|
||||
// TODO: Use okhttp redirect and add this check again
|
||||
// return reply.httpCode == HttpConstants.HTTP_OK &&
|
||||
return !reply.isException &&
|
||||
reply.data != null
|
||||
}
|
||||
|
||||
private fun getOwnCloudStatus(client: OwnCloudClient): RemoteOperationResult<RemoteServerInfo> {
|
||||
val remoteStatusOperation = GetRemoteStatusOperation()
|
||||
return remoteStatusOperation.execute(client)
|
||||
}
|
||||
|
||||
private fun canAccessRootFolder(client: OwnCloudClient): RemoteOperationResult<Boolean> {
|
||||
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation("/", true)
|
||||
return checkPathExistenceRemoteOperation.execute(client)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if credentials should be invalidated according the to the HTTPS status
|
||||
* of a network request just performed.
|
||||
*
|
||||
* @param httpStatusCode Result of the last request ran with the 'credentials' belows.
|
||||
* @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or
|
||||
* cannot be invalidated with the given arguments.
|
||||
*/
|
||||
private fun shouldInvalidateAccountCredentials(credentials: OwnCloudCredentials, account: OwnCloudAccount, httpStatusCode: Int): Boolean {
|
||||
var shouldInvalidateAccountCredentials = httpStatusCode == HttpConstants.HTTP_UNAUTHORIZED
|
||||
shouldInvalidateAccountCredentials = shouldInvalidateAccountCredentials and // real credentials
|
||||
(credentials !is OwnCloudAnonymousCredentials)
|
||||
|
||||
// test if have all the needed to effectively invalidate ...
|
||||
shouldInvalidateAccountCredentials =
|
||||
shouldInvalidateAccountCredentials and (account.savedAccount != null)
|
||||
Timber.d(
|
||||
"""Received error: $httpStatusCode,
|
||||
account: ${account.name}
|
||||
credentials are real: ${credentials !is OwnCloudAnonymousCredentials},
|
||||
so we need to invalidate credentials for account ${account.name} : $shouldInvalidateAccountCredentials"""
|
||||
)
|
||||
return shouldInvalidateAccountCredentials
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates credentials stored for the given account in the system [AccountManager] and in
|
||||
* current [SingleSessionManager.getDefaultSingleton] instance.
|
||||
*
|
||||
*
|
||||
* [.shouldInvalidateAccountCredentials] should be called first.
|
||||
*
|
||||
*/
|
||||
private fun invalidateAccountCredentials(account: OwnCloudAccount, credentials: OwnCloudCredentials) {
|
||||
Timber.i("Invalidating account credentials for account $account")
|
||||
val am = AccountManager.get(context)
|
||||
am.invalidateAuthToken(
|
||||
account.savedAccount.type,
|
||||
credentials.authToken
|
||||
)
|
||||
am.clearPassword(account.savedAccount) // being strict, only needed for Basic Auth credentials
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the status code of an execution and decides if should be repeated with fresh credentials.
|
||||
*
|
||||
*
|
||||
* Invalidates current credentials if the request failed as anauthorized.
|
||||
*
|
||||
*
|
||||
* Refresh current credentials if possible, and marks a retry.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private fun checkUnauthorizedAccess(client: OwnCloudClient, singleSessionManager: SingleSessionManager, status: Int): Boolean {
|
||||
var credentialsWereRefreshed = false
|
||||
val account = client.account
|
||||
val credentials = account.credentials
|
||||
if (shouldInvalidateAccountCredentials(credentials, account, status)) {
|
||||
invalidateAccountCredentials(account, credentials)
|
||||
|
||||
if (credentials.authTokenCanBeRefreshed()) {
|
||||
try {
|
||||
// This command does the actual refresh
|
||||
Timber.i("Trying to refresh auth token for account $account")
|
||||
account.loadCredentials(context)
|
||||
// if mAccount.getCredentials().length() == 0 --> refresh failed
|
||||
client.credentials = account.credentials
|
||||
credentialsWereRefreshed = true
|
||||
} catch (e: AccountsException) {
|
||||
Timber.e(
|
||||
e, "Error while trying to refresh auth token for %s\ntrace: %s",
|
||||
account.savedAccount.name,
|
||||
ExceptionUtils.getStackTrace(e)
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
Timber.e(
|
||||
e, "Error while trying to refresh auth token for %s\ntrace: %s",
|
||||
account.savedAccount.name,
|
||||
ExceptionUtils.getStackTrace(e)
|
||||
)
|
||||
}
|
||||
if (!credentialsWereRefreshed) {
|
||||
// if credentials are not refreshed, client must be removed
|
||||
// from the OwnCloudClientManager to prevent it is reused once and again
|
||||
Timber.w("Credentials were not refreshed, client will be removed from the Session Manager to prevent using it over and over")
|
||||
singleSessionManager.removeClientFor(account)
|
||||
}
|
||||
}
|
||||
// else: onExecute will finish with status 401
|
||||
}
|
||||
return credentialsWereRefreshed
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val VALIDATION_RETRY_COUNT = 3
|
||||
}
|
||||
}
|
@ -24,12 +24,6 @@
|
||||
|
||||
package com.owncloud.android.lib.common;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AuthenticatorException;
|
||||
@ -37,6 +31,13 @@ import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* OwnCloud Account
|
||||
*
|
||||
@ -54,10 +55,9 @@ public class OwnCloudAccount {
|
||||
|
||||
private Account mSavedAccount;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for already saved OC accounts.
|
||||
*
|
||||
* <p>
|
||||
* Do not use for anonymous credentials.
|
||||
*/
|
||||
public OwnCloudAccount(Account savedAccount, Context context) throws AccountNotFoundException {
|
||||
@ -82,7 +82,6 @@ public class OwnCloudAccount {
|
||||
mDisplayName = ama.getUserData(mSavedAccount, AccountUtils.Constants.KEY_DISPLAY_NAME);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for non yet saved OC accounts.
|
||||
*
|
||||
@ -104,19 +103,15 @@ public class OwnCloudAccount {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method for deferred load of account attributes from AccountManager
|
||||
*
|
||||
* @param context
|
||||
* @throws AccountNotFoundException
|
||||
* @throws AuthenticatorException
|
||||
* @throws IOException
|
||||
* @throws OperationCanceledException
|
||||
*/
|
||||
public void loadCredentials(Context context)
|
||||
throws AccountNotFoundException, AuthenticatorException,
|
||||
IOException, OperationCanceledException {
|
||||
public void loadCredentials(Context context) throws AuthenticatorException, IOException, OperationCanceledException {
|
||||
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("Parameter 'context' cannot be null");
|
||||
@ -154,5 +149,4 @@ public class OwnCloudAccount {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,255 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
* Copyright (C) 2012 Bartek Przybylski
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials;
|
||||
import com.owncloud.android.lib.common.http.HttpClient;
|
||||
import com.owncloud.android.lib.common.http.HttpConstants;
|
||||
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
|
||||
import com.owncloud.android.lib.common.utils.RandomUtils;
|
||||
import okhttp3.Cookie;
|
||||
import okhttp3.HttpUrl;
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER;
|
||||
import static com.owncloud.android.lib.common.http.HttpConstants.HTTP_MOVED_PERMANENTLY;
|
||||
|
||||
public class OwnCloudClient extends HttpClient {
|
||||
|
||||
public static final String WEBDAV_FILES_PATH_4_0 = "/remote.php/dav/files/";
|
||||
public static final String STATUS_PATH = "/status.php";
|
||||
private static final String WEBDAV_UPLOADS_PATH_4_0 = "/remote.php/dav/uploads/";
|
||||
private static final int MAX_RETRY_COUNT = 2;
|
||||
|
||||
private static int sIntanceCounter = 0;
|
||||
private OwnCloudCredentials mCredentials = null;
|
||||
private int mInstanceNumber;
|
||||
private Uri mBaseUri;
|
||||
private OwnCloudAccount mAccount;
|
||||
private final ConnectionValidator mConnectionValidator;
|
||||
private Object mRequestMutex = new Object();
|
||||
|
||||
// If set to true a mutex will be used to prevent parallel execution of the execute() method
|
||||
// if false the execute() method can be called even though the mutex is already aquired.
|
||||
// This is used for the ConnectionValidator, which has to be able to execute OperationsWhile all "normal" operations net
|
||||
// to be set on hold.
|
||||
private final Boolean mSynchronizeRequests;
|
||||
|
||||
private SingleSessionManager mSingleSessionManager = null;
|
||||
|
||||
private boolean mFollowRedirects = false;
|
||||
|
||||
public OwnCloudClient(Uri baseUri,
|
||||
ConnectionValidator connectionValidator,
|
||||
boolean synchronizeRequests,
|
||||
SingleSessionManager singleSessionManager,
|
||||
Context context) {
|
||||
super(context);
|
||||
|
||||
if (baseUri == null) {
|
||||
throw new IllegalArgumentException("Parameter 'baseUri' cannot be NULL");
|
||||
}
|
||||
mBaseUri = baseUri;
|
||||
mSynchronizeRequests = synchronizeRequests;
|
||||
mSingleSessionManager = singleSessionManager;
|
||||
|
||||
mInstanceNumber = sIntanceCounter++;
|
||||
Timber.d("#" + mInstanceNumber + "Creating OwnCloudClient");
|
||||
|
||||
clearCredentials();
|
||||
clearCookies();
|
||||
mConnectionValidator = connectionValidator;
|
||||
}
|
||||
|
||||
public void clearCredentials() {
|
||||
if (!(mCredentials instanceof OwnCloudAnonymousCredentials)) {
|
||||
mCredentials = OwnCloudCredentialsFactory.getAnonymousCredentials();
|
||||
}
|
||||
}
|
||||
|
||||
public int executeHttpMethod(HttpBaseMethod method) throws Exception {
|
||||
if (mSynchronizeRequests) {
|
||||
synchronized (mRequestMutex) {
|
||||
return saveExecuteHttpMethod(method);
|
||||
}
|
||||
} else {
|
||||
return saveExecuteHttpMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
private int saveExecuteHttpMethod(HttpBaseMethod method) throws Exception {
|
||||
int repeatCounter = 0;
|
||||
int status;
|
||||
|
||||
if (mFollowRedirects) {
|
||||
method.setFollowRedirects(true);
|
||||
}
|
||||
|
||||
boolean retry;
|
||||
do {
|
||||
repeatCounter++;
|
||||
retry = false;
|
||||
String requestId = RandomUtils.generateRandomUUID();
|
||||
|
||||
// Header to allow tracing requests in apache and ownCloud logs
|
||||
Timber.d("Executing in request with id %s", requestId);
|
||||
method.setRequestHeader(HttpConstants.OC_X_REQUEST_ID, requestId);
|
||||
method.setRequestHeader(HttpConstants.USER_AGENT_HEADER, SingleSessionManager.getUserAgent());
|
||||
method.setRequestHeader(HttpConstants.ACCEPT_LANGUAGE_HEADER, Locale.getDefault().getLanguage());
|
||||
method.setRequestHeader(HttpConstants.ACCEPT_ENCODING_HEADER, HttpConstants.ACCEPT_ENCODING_IDENTITY);
|
||||
if (mCredentials.getHeaderAuth() != null && !mCredentials.getHeaderAuth().isEmpty()) {
|
||||
method.setRequestHeader(AUTHORIZATION_HEADER, mCredentials.getHeaderAuth());
|
||||
}
|
||||
|
||||
status = method.execute(this);
|
||||
|
||||
if (shouldConnectionValidatorBeCalled(method, status)) {
|
||||
retry = mConnectionValidator.validate(this, mSingleSessionManager, getContext()); // retry on success fail on no success
|
||||
} else if (method.getFollowPermanentRedirects() && status == HTTP_MOVED_PERMANENTLY) {
|
||||
retry = true;
|
||||
method.setFollowRedirects(true);
|
||||
}
|
||||
|
||||
} while (retry && repeatCounter < MAX_RETRY_COUNT);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private boolean shouldConnectionValidatorBeCalled(HttpBaseMethod method, int status) {
|
||||
|
||||
return mConnectionValidator != null && (
|
||||
(!(mCredentials instanceof OwnCloudAnonymousCredentials) &&
|
||||
status == HttpConstants.HTTP_UNAUTHORIZED
|
||||
) || (!mFollowRedirects &&
|
||||
!method.getFollowRedirects() &&
|
||||
status == HttpConstants.HTTP_MOVED_TEMPORARILY
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation.
|
||||
*
|
||||
* @param responseBodyAsStream InputStream with the HTTP response to exhaust.
|
||||
*/
|
||||
public void exhaustResponse(InputStream responseBodyAsStream) {
|
||||
if (responseBodyAsStream != null) {
|
||||
try {
|
||||
responseBodyAsStream.close();
|
||||
|
||||
} catch (IOException io) {
|
||||
Timber.e(io, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Uri getBaseFilesWebDavUri() {
|
||||
return Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0);
|
||||
}
|
||||
|
||||
public Uri getUserFilesWebDavUri() {
|
||||
return (mCredentials instanceof OwnCloudAnonymousCredentials || mAccount == null)
|
||||
? Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0)
|
||||
: Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0 + AccountUtils.getUserId(
|
||||
mAccount.getSavedAccount(), getContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public Uri getUploadsWebDavUri() {
|
||||
return mCredentials instanceof OwnCloudAnonymousCredentials
|
||||
? Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0)
|
||||
: Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0 + AccountUtils.getUserId(
|
||||
mAccount.getSavedAccount(), getContext()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public Uri getBaseUri() {
|
||||
return mBaseUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the root URI to the ownCloud server.
|
||||
* <p>
|
||||
* Use with care.
|
||||
*
|
||||
* @param uri
|
||||
*/
|
||||
public void setBaseUri(Uri uri) {
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("URI cannot be NULL");
|
||||
}
|
||||
mBaseUri = uri;
|
||||
}
|
||||
|
||||
public final OwnCloudCredentials getCredentials() {
|
||||
return mCredentials;
|
||||
}
|
||||
|
||||
public void setCredentials(OwnCloudCredentials credentials) {
|
||||
if (credentials != null) {
|
||||
mCredentials = credentials;
|
||||
} else {
|
||||
clearCredentials();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCookiesForBaseUri(List<Cookie> cookies) {
|
||||
getOkHttpClient().cookieJar().saveFromResponse(
|
||||
HttpUrl.parse(mBaseUri.toString()),
|
||||
cookies
|
||||
);
|
||||
}
|
||||
|
||||
public List<Cookie> getCookiesForBaseUri() {
|
||||
return getOkHttpClient().cookieJar().loadForRequest(
|
||||
HttpUrl.parse(mBaseUri.toString()));
|
||||
}
|
||||
|
||||
public OwnCloudAccount getAccount() {
|
||||
return mAccount;
|
||||
}
|
||||
|
||||
public void setAccount(OwnCloudAccount account) {
|
||||
this.mAccount = account;
|
||||
}
|
||||
|
||||
public void setFollowRedirects(boolean followRedirects) {
|
||||
this.mFollowRedirects = followRedirects;
|
||||
}
|
||||
}
|
@ -0,0 +1,225 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common;
|
||||
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* @author David A. Velasco
|
||||
* @author masensio
|
||||
* @author Christian Schabesberger
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
|
||||
public class SingleSessionManager {
|
||||
|
||||
private static SingleSessionManager sDefaultSingleton;
|
||||
private static String sUserAgent;
|
||||
private static ConnectionValidator sConnectionValidator;
|
||||
|
||||
private ConcurrentMap<String, OwnCloudClient> mClientsWithKnownUsername = new ConcurrentHashMap<>();
|
||||
private ConcurrentMap<String, OwnCloudClient> mClientsWithUnknownUsername = new ConcurrentHashMap<>();
|
||||
|
||||
public static SingleSessionManager getDefaultSingleton() {
|
||||
if (sDefaultSingleton == null) {
|
||||
sDefaultSingleton = new SingleSessionManager();
|
||||
}
|
||||
return sDefaultSingleton;
|
||||
}
|
||||
|
||||
public static void setConnectionValidator(ConnectionValidator connectionValidator) {
|
||||
sConnectionValidator = connectionValidator;
|
||||
}
|
||||
|
||||
public static ConnectionValidator getConnectionValidator() {
|
||||
return sConnectionValidator;
|
||||
}
|
||||
|
||||
public static String getUserAgent() {
|
||||
return sUserAgent;
|
||||
}
|
||||
|
||||
public static void setUserAgent(String userAgent) {
|
||||
sUserAgent = userAgent;
|
||||
}
|
||||
|
||||
private static OwnCloudClient createOwnCloudClient(Uri uri,
|
||||
Context context,
|
||||
ConnectionValidator connectionValidator,
|
||||
SingleSessionManager singleSessionManager) {
|
||||
OwnCloudClient client = new OwnCloudClient(uri, connectionValidator, true, singleSessionManager, context);
|
||||
return client;
|
||||
}
|
||||
|
||||
public OwnCloudClient getClientFor(OwnCloudAccount account,
|
||||
Context context) throws OperationCanceledException,
|
||||
AuthenticatorException, IOException {
|
||||
return getClientFor(account, context, getConnectionValidator());
|
||||
}
|
||||
|
||||
public OwnCloudClient getClientFor(OwnCloudAccount account,
|
||||
Context context,
|
||||
ConnectionValidator connectionValidator) throws OperationCanceledException,
|
||||
AuthenticatorException, IOException {
|
||||
|
||||
Timber.d("getClientFor starting ");
|
||||
if (account == null) {
|
||||
throw new IllegalArgumentException("Cannot get an OwnCloudClient for a null account");
|
||||
}
|
||||
|
||||
OwnCloudClient client = null;
|
||||
String accountName = account.getName();
|
||||
String sessionName = account.getCredentials() == null ? "" :
|
||||
AccountUtils.buildAccountName(account.getBaseUri(), account.getCredentials().getAuthToken());
|
||||
|
||||
if (accountName != null) {
|
||||
client = mClientsWithKnownUsername.get(accountName);
|
||||
}
|
||||
boolean reusingKnown = false; // just for logs
|
||||
if (client == null) {
|
||||
if (accountName != null) {
|
||||
client = mClientsWithUnknownUsername.remove(sessionName);
|
||||
if (client != null) {
|
||||
Timber.v("reusing client for session %s", sessionName);
|
||||
|
||||
mClientsWithKnownUsername.put(accountName, client);
|
||||
Timber.v("moved client to account %s", accountName);
|
||||
}
|
||||
} else {
|
||||
client = mClientsWithUnknownUsername.get(sessionName);
|
||||
}
|
||||
} else {
|
||||
Timber.v("reusing client for account %s", accountName);
|
||||
if (client.getAccount() != null &&
|
||||
client.getAccount().getCredentials() != null &&
|
||||
(client.getAccount().getCredentials().getAuthToken() == null || client.getAccount().getCredentials().getAuthToken().isEmpty())
|
||||
) {
|
||||
Timber.i("Client " + client.getAccount().getName() + " needs to refresh credentials");
|
||||
|
||||
//the next two lines are a hack because okHttpclient is used as a singleton instead of being an
|
||||
//injected instance that can be deleted when required
|
||||
client.clearCookies();
|
||||
client.clearCredentials();
|
||||
|
||||
client.setAccount(account);
|
||||
|
||||
account.loadCredentials(context);
|
||||
client.setCredentials(account.getCredentials());
|
||||
|
||||
Timber.i("Client " + account.getName() + " with credentials size" + client.getAccount().getCredentials().getAuthToken().length());
|
||||
}
|
||||
reusingKnown = true;
|
||||
}
|
||||
|
||||
if (client == null) {
|
||||
// no client to reuse - create a new one
|
||||
client = createOwnCloudClient(
|
||||
account.getBaseUri(),
|
||||
context,
|
||||
connectionValidator,
|
||||
this); // TODO remove dependency on OwnCloudClientFactory
|
||||
|
||||
//the next two lines are a hack because okHttpclient is used as a singleton instead of being an
|
||||
//injected instance that can be deleted when required
|
||||
client.clearCookies();
|
||||
client.clearCredentials();
|
||||
|
||||
client.setAccount(account);
|
||||
|
||||
account.loadCredentials(context);
|
||||
client.setCredentials(account.getCredentials());
|
||||
|
||||
if (accountName != null) {
|
||||
mClientsWithKnownUsername.put(accountName, client);
|
||||
Timber.v("new client for account %s", accountName);
|
||||
|
||||
} else {
|
||||
mClientsWithUnknownUsername.put(sessionName, client);
|
||||
Timber.v("new client for session %s", sessionName);
|
||||
}
|
||||
} else {
|
||||
if (!reusingKnown) {
|
||||
Timber.v("reusing client for session %s", sessionName);
|
||||
}
|
||||
|
||||
keepUriUpdated(account, client);
|
||||
}
|
||||
Timber.d("getClientFor finishing ");
|
||||
return client;
|
||||
}
|
||||
|
||||
public void removeClientFor(OwnCloudAccount account) {
|
||||
Timber.d("removeClientFor starting ");
|
||||
|
||||
if (account == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OwnCloudClient client;
|
||||
String accountName = account.getName();
|
||||
if (accountName != null) {
|
||||
client = mClientsWithKnownUsername.remove(accountName);
|
||||
if (client != null) {
|
||||
Timber.v("Removed client for account %s", accountName);
|
||||
return;
|
||||
} else {
|
||||
Timber.v("No client tracked for account %s", accountName);
|
||||
}
|
||||
}
|
||||
|
||||
mClientsWithUnknownUsername.clear();
|
||||
|
||||
Timber.d("removeClientFor finishing ");
|
||||
}
|
||||
|
||||
public void refreshCredentialsForAccount(String accountName, OwnCloudCredentials credentials) {
|
||||
OwnCloudClient ownCloudClient = mClientsWithKnownUsername.get(accountName);
|
||||
if (ownCloudClient == null) {
|
||||
return;
|
||||
}
|
||||
ownCloudClient.setCredentials(credentials);
|
||||
mClientsWithKnownUsername.replace(accountName, ownCloudClient);
|
||||
}
|
||||
|
||||
// this method is just a patch; we need to distinguish accounts in the same host but
|
||||
// different paths; but that requires updating the accountNames for apps upgrading
|
||||
private void keepUriUpdated(OwnCloudAccount account, OwnCloudClient reusedClient) {
|
||||
Uri recentUri = account.getBaseUri();
|
||||
if (!recentUri.equals(reusedClient.getBaseUri())) {
|
||||
reusedClient.setBaseUri(recentUri);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -41,9 +41,4 @@ public class AccountTypeUtils {
|
||||
public static String getAuthTokenTypeRefreshToken(String accountType) {
|
||||
return accountType + ".oauth2.refresh_token";
|
||||
}
|
||||
|
||||
public static String getAuthTokenTypeSamlSessionCookie(String accountType) {
|
||||
return accountType + ".saml.web_sso.session_cookie";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,224 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
* Copyright (C) 2012 Bartek Przybylski
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.accounts;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AccountsException;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentials;
|
||||
import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory;
|
||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class AccountUtils {
|
||||
/**
|
||||
* Constructs full url to host and webdav resource basing on host version
|
||||
*
|
||||
* @param context Valid Android {@link Context}, needed to access the {@link AccountManager}
|
||||
* @param account A stored ownCloud {@link Account}
|
||||
* @return Full URL to WebDAV endpoint in the server corresponding to 'account'.
|
||||
* @throws AccountNotFoundException When 'account' is unknown for the AccountManager
|
||||
*/
|
||||
public static String getWebDavUrlForAccount(Context context, Account account)
|
||||
throws AccountNotFoundException {
|
||||
|
||||
return getBaseUrlForAccount(context, account) + OwnCloudClient.WEBDAV_FILES_PATH_4_0
|
||||
+ AccountUtils.getUserId(account, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts url server from the account
|
||||
*
|
||||
* @param context Valid Android {@link Context}, needed to access the {@link AccountManager}
|
||||
* @param account A stored ownCloud {@link Account}
|
||||
* @return Full URL to the server corresponding to 'account', ending in the base path
|
||||
* common to all API endpoints.
|
||||
* @throws AccountNotFoundException When 'account' is unknown for the AccountManager
|
||||
*/
|
||||
public static String getBaseUrlForAccount(Context context, Account account)
|
||||
throws AccountNotFoundException {
|
||||
AccountManager ama = AccountManager.get(context.getApplicationContext());
|
||||
String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL);
|
||||
|
||||
if (baseurl == null) {
|
||||
throw new AccountNotFoundException(account, "Account not found", null);
|
||||
}
|
||||
|
||||
return baseurl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the username corresponding to an OC account.
|
||||
*
|
||||
* @param account An OC account
|
||||
* @return Username for the given account, extracted from the account.name
|
||||
*/
|
||||
public static String getUsernameForAccount(Account account) {
|
||||
String username = null;
|
||||
try {
|
||||
username = account.name.substring(0, account.name.lastIndexOf('@'));
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Couldn't get a username for the given account");
|
||||
}
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws AuthenticatorException
|
||||
* @throws OperationCanceledException
|
||||
*/
|
||||
public static OwnCloudCredentials getCredentialsForAccount(Context context, Account account)
|
||||
throws OperationCanceledException, AuthenticatorException, IOException {
|
||||
|
||||
OwnCloudCredentials credentials;
|
||||
AccountManager am = AccountManager.get(context);
|
||||
|
||||
String supportsOAuth2 = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_OAUTH2);
|
||||
boolean isOauth2 = supportsOAuth2 != null && supportsOAuth2.equals(Constants.OAUTH_SUPPORTED_TRUE);
|
||||
|
||||
String username = AccountUtils.getUsernameForAccount(account);
|
||||
|
||||
if (isOauth2) {
|
||||
Timber.i("Trying to retrieve credentials for oAuth account" + account.name);
|
||||
String accessToken = am.blockingGetAuthToken(
|
||||
account,
|
||||
AccountTypeUtils.getAuthTokenTypeAccessToken(account.type),
|
||||
false);
|
||||
|
||||
credentials = OwnCloudCredentialsFactory.newBearerCredentials(username, accessToken);
|
||||
} else {
|
||||
String password = am.blockingGetAuthToken(
|
||||
account,
|
||||
AccountTypeUtils.getAuthTokenTypePass(account.type),
|
||||
false);
|
||||
|
||||
credentials = OwnCloudCredentialsFactory.newBasicCredentials(
|
||||
username,
|
||||
password
|
||||
);
|
||||
}
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user id corresponding to an OC account.
|
||||
*
|
||||
* @param account ownCloud account
|
||||
* @return user id
|
||||
*/
|
||||
public static String getUserId(Account account, Context context) {
|
||||
AccountManager accountMgr = AccountManager.get(context);
|
||||
return accountMgr.getUserData(account, Constants.KEY_ID);
|
||||
}
|
||||
|
||||
public static String buildAccountNameOld(Uri serverBaseUrl, String username) {
|
||||
if (serverBaseUrl.getScheme() == null) {
|
||||
serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString());
|
||||
}
|
||||
String accountName = username + "@" + serverBaseUrl.getHost();
|
||||
if (serverBaseUrl.getPort() >= 0) {
|
||||
accountName += ":" + serverBaseUrl.getPort();
|
||||
}
|
||||
return accountName;
|
||||
}
|
||||
|
||||
public static String buildAccountName(Uri serverBaseUrl, String username) {
|
||||
if (serverBaseUrl.getScheme() == null) {
|
||||
serverBaseUrl = Uri.parse("https://" + serverBaseUrl.toString());
|
||||
}
|
||||
|
||||
// Remove http:// or https://
|
||||
String url = serverBaseUrl.toString();
|
||||
if (url.contains("://")) {
|
||||
url = url.substring(serverBaseUrl.toString().indexOf("://") + 3);
|
||||
}
|
||||
|
||||
return username + "@" + url;
|
||||
}
|
||||
|
||||
public static class AccountNotFoundException extends AccountsException {
|
||||
|
||||
/**
|
||||
* Generated - should be refreshed every time the class changes!!
|
||||
*/
|
||||
private static final long serialVersionUID = -1684392454798508693L;
|
||||
|
||||
private Account mFailedAccount;
|
||||
|
||||
public AccountNotFoundException(Account failedAccount, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
mFailedAccount = failedAccount;
|
||||
}
|
||||
|
||||
public Account getFailedAccount() {
|
||||
return mFailedAccount;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Constants {
|
||||
/**
|
||||
* Base url should point to owncloud installation without trailing / ie:
|
||||
* http://server/path or https://owncloud.server
|
||||
*/
|
||||
public static final String KEY_OC_BASE_URL = "oc_base_url";
|
||||
/**
|
||||
* Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
|
||||
*/
|
||||
|
||||
// TODO Please review this constants, move them out of the library, the rest of OAuth variables are in data layer
|
||||
public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";
|
||||
|
||||
public static final String OAUTH_SUPPORTED_TRUE = "TRUE";
|
||||
|
||||
/**
|
||||
* OC account version
|
||||
*/
|
||||
public static final String KEY_OC_ACCOUNT_VERSION = "oc_account_version";
|
||||
|
||||
/**
|
||||
* User's id
|
||||
*/
|
||||
public static final String KEY_ID = "oc_id";
|
||||
|
||||
/**
|
||||
* User's display name
|
||||
*/
|
||||
public static final String KEY_DISPLAY_NAME = "oc_display_name";
|
||||
|
||||
public static final int ACCOUNT_VERSION = 1;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -21,45 +21,20 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common;
|
||||
package com.owncloud.android.lib.common.authentication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import okhttp3.Credentials;
|
||||
|
||||
import org.apache.commons.httpclient.UsernamePasswordCredentials;
|
||||
import org.apache.commons.httpclient.auth.AuthPolicy;
|
||||
import org.apache.commons.httpclient.auth.AuthScope;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class OwnCloudBasicCredentials implements OwnCloudCredentials {
|
||||
|
||||
private String mUsername;
|
||||
private String mPassword;
|
||||
private boolean mAuthenticationPreemptive;
|
||||
|
||||
public OwnCloudBasicCredentials(String username, String password) {
|
||||
mUsername = username != null ? username : "";
|
||||
mPassword = password != null ? password : "";
|
||||
mAuthenticationPreemptive = true;
|
||||
}
|
||||
|
||||
public OwnCloudBasicCredentials(String username, String password, boolean preemptiveMode) {
|
||||
mUsername = username != null ? username : "";
|
||||
mPassword = password != null ? password : "";
|
||||
mAuthenticationPreemptive = preemptiveMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(OwnCloudClient client) {
|
||||
List<String> authPrefs = new ArrayList<String>(1);
|
||||
authPrefs.add(AuthPolicy.BASIC);
|
||||
client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
|
||||
|
||||
client.getParams().setAuthenticationPreemptive(mAuthenticationPreemptive);
|
||||
client.getParams().setCredentialCharset(OwnCloudCredentialsFactory.CREDENTIAL_CHARSET);
|
||||
client.getState().setCredentials(
|
||||
AuthScope.ANY,
|
||||
new UsernamePasswordCredentials(mUsername, mPassword)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -72,9 +47,18 @@ public class OwnCloudBasicCredentials implements OwnCloudCredentials {
|
||||
return mPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeaderAuth() {
|
||||
return Credentials.basic(mUsername, mPassword, UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authTokenExpires() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authTokenCanBeRefreshed() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* @author masensio
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -22,62 +21,43 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.status;
|
||||
package com.owncloud.android.lib.common.authentication;
|
||||
|
||||
/**
|
||||
* Enum for Boolean Type in OCCapability parameters, with values:
|
||||
* -1 - Unknown
|
||||
* 0 - False
|
||||
* 1 - True
|
||||
*/
|
||||
public enum CapabilityBooleanType {
|
||||
UNKNOWN (-1),
|
||||
FALSE (0),
|
||||
TRUE (1);
|
||||
import com.owncloud.android.lib.common.http.HttpConstants;
|
||||
|
||||
private int value;
|
||||
public class OwnCloudBearerCredentials implements OwnCloudCredentials {
|
||||
|
||||
CapabilityBooleanType(int value)
|
||||
{
|
||||
this.value = value;
|
||||
private String mUsername;
|
||||
private String mAccessToken;
|
||||
|
||||
public OwnCloudBearerCredentials(String username, String accessToken) {
|
||||
mUsername = username != null ? username : "";
|
||||
mAccessToken = accessToken != null ? accessToken : "";
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
@Override
|
||||
public String getUsername() {
|
||||
// not relevant for authentication, but relevant for informational purposes
|
||||
return mUsername;
|
||||
}
|
||||
|
||||
public static CapabilityBooleanType fromValue(int value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case -1:
|
||||
return UNKNOWN;
|
||||
case 0:
|
||||
return FALSE;
|
||||
case 1:
|
||||
return TRUE;
|
||||
}
|
||||
return null;
|
||||
@Override
|
||||
public String getAuthToken() {
|
||||
return mAccessToken;
|
||||
}
|
||||
|
||||
public static CapabilityBooleanType fromBooleanValue(boolean boolValue){
|
||||
if (boolValue){
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
@Override
|
||||
public String getHeaderAuth() {
|
||||
return HttpConstants.BEARER_AUTHORIZATION_KEY + mAccessToken;
|
||||
}
|
||||
|
||||
public boolean isUnknown(){
|
||||
return getValue() == -1;
|
||||
@Override
|
||||
public boolean authTokenExpires() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isFalse(){
|
||||
return getValue() == 0;
|
||||
@Override
|
||||
public boolean authTokenCanBeRefreshed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isTrue(){
|
||||
return getValue() == 1;
|
||||
}
|
||||
|
||||
};
|
@ -22,16 +22,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common;
|
||||
package com.owncloud.android.lib.common.authentication;
|
||||
|
||||
public interface OwnCloudCredentials {
|
||||
|
||||
public void applyTo(OwnCloudClient ownCloudClient);
|
||||
String getUsername();
|
||||
|
||||
public String getUsername();
|
||||
String getAuthToken();
|
||||
|
||||
public String getAuthToken();
|
||||
String getHeaderAuth();
|
||||
|
||||
public boolean authTokenExpires();
|
||||
boolean authTokenExpires();
|
||||
|
||||
boolean authTokenCanBeRefreshed();
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -22,7 +22,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common;
|
||||
package com.owncloud.android.lib.common.authentication;
|
||||
|
||||
public class OwnCloudCredentialsFactory {
|
||||
|
||||
@ -34,18 +34,8 @@ public class OwnCloudCredentialsFactory {
|
||||
return new OwnCloudBasicCredentials(username, password);
|
||||
}
|
||||
|
||||
public static OwnCloudCredentials newBasicCredentials(
|
||||
String username, String password, boolean preemptiveMode
|
||||
) {
|
||||
return new OwnCloudBasicCredentials(username, password, preemptiveMode);
|
||||
}
|
||||
|
||||
public static OwnCloudCredentials newBearerCredentials(String authToken) {
|
||||
return new OwnCloudBearerCredentials(authToken);
|
||||
}
|
||||
|
||||
public static OwnCloudCredentials newSamlSsoCredentials(String username, String sessionCookie) {
|
||||
return new OwnCloudSamlSsoCredentials(username, sessionCookie);
|
||||
public static OwnCloudCredentials newBearerCredentials(String username, String authToken) {
|
||||
return new OwnCloudBearerCredentials(username, authToken);
|
||||
}
|
||||
|
||||
public static final OwnCloudCredentials getAnonymousCredentials() {
|
||||
@ -61,13 +51,12 @@ public class OwnCloudCredentialsFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTo(OwnCloudClient client) {
|
||||
client.getState().clearCredentials();
|
||||
client.getState().clearCookies();
|
||||
public String getAuthToken() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthToken() {
|
||||
public String getHeaderAuth() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -76,11 +65,15 @@ public class OwnCloudCredentialsFactory {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean authTokenCanBeRefreshed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
// no user name
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2021 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http
|
||||
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
class CookieJarImpl(
|
||||
private val cookieStore: HashMap<String, List<Cookie>>
|
||||
) : CookieJar {
|
||||
|
||||
fun containsCookieWithName(cookies: List<Cookie>, name: String): Boolean {
|
||||
for (cookie: Cookie in cookies) {
|
||||
if (cookie.name == name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun getUpdatedCookies(oldCookies: List<Cookie>, newCookies: List<Cookie>): List<Cookie> {
|
||||
val updatedList = ArrayList<Cookie>(newCookies)
|
||||
for (oldCookie: Cookie in oldCookies) {
|
||||
if (!containsCookieWithName(updatedList, oldCookie.name)) {
|
||||
updatedList.add(oldCookie)
|
||||
}
|
||||
}
|
||||
return updatedList
|
||||
}
|
||||
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||
// Avoid duplicated cookies but update
|
||||
val currentCookies: List<Cookie> = cookieStore[url.host] ?: ArrayList()
|
||||
val updatedCookies: List<Cookie> = getUpdatedCookies(currentCookies, cookies)
|
||||
cookieStore[url.host] = updatedCookies
|
||||
}
|
||||
|
||||
override fun loadForRequest(url: HttpUrl) =
|
||||
cookieStore[url.host] ?: ArrayList()
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.http
|
||||
|
||||
import okhttp3.Interceptor
|
||||
|
||||
class DummyInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain) = chain.proceed(chain.request())
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.http;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.owncloud.android.lib.common.network.AdvancedX509TrustManager;
|
||||
import com.owncloud.android.lib.common.network.NetworkUtils;
|
||||
import okhttp3.Cookie;
|
||||
import okhttp3.CookieJar;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Protocol;
|
||||
import okhttp3.TlsVersion;
|
||||
import timber.log.Timber;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Client used to perform network operations
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
|
||||
public class HttpClient {
|
||||
private Context mContext;
|
||||
private HashMap<String, List<Cookie>> mCookieStore = new HashMap<>();
|
||||
private LogInterceptor mLogInterceptor = new LogInterceptor();
|
||||
|
||||
private OkHttpClient mOkHttpClient = null;
|
||||
|
||||
protected HttpClient(Context context) {
|
||||
if (context == null) {
|
||||
Timber.e("Context may not be NULL!");
|
||||
throw new NullPointerException("Context may not be NULL!");
|
||||
}
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public OkHttpClient getOkHttpClient() {
|
||||
if (mOkHttpClient == null) {
|
||||
try {
|
||||
final X509TrustManager trustManager = new AdvancedX509TrustManager(
|
||||
NetworkUtils.getKnownServersStore(mContext));
|
||||
|
||||
final SSLContext sslContext = buildSSLContext();
|
||||
sslContext.init(null, new TrustManager[]{trustManager}, null);
|
||||
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||
|
||||
// Automatic cookie handling, NOT PERSISTENT
|
||||
final CookieJar cookieJar = new CookieJarImpl(mCookieStore);
|
||||
mOkHttpClient = buildNewOkHttpClient(sslSocketFactory, trustManager, cookieJar);
|
||||
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
Timber.e(nsae, "Could not setup SSL system.");
|
||||
throw new RuntimeException("Could not setup okHttp client.", nsae);
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Could not setup okHttp client.");
|
||||
throw new RuntimeException("Could not setup okHttp client.", e);
|
||||
}
|
||||
}
|
||||
return mOkHttpClient;
|
||||
}
|
||||
|
||||
private SSLContext buildSSLContext() throws NoSuchAlgorithmException {
|
||||
try {
|
||||
return SSLContext.getInstance(TlsVersion.TLS_1_3.javaName());
|
||||
} catch (NoSuchAlgorithmException tlsv13Exception) {
|
||||
try {
|
||||
Timber.w("TLSv1.3 is not supported in this device; falling through TLSv1.2");
|
||||
return SSLContext.getInstance(TlsVersion.TLS_1_2.javaName());
|
||||
} catch (NoSuchAlgorithmException tlsv12Exception) {
|
||||
try {
|
||||
Timber.w("TLSv1.2 is not supported in this device; falling through TLSv1.1");
|
||||
return SSLContext.getInstance(TlsVersion.TLS_1_1.javaName());
|
||||
} catch (NoSuchAlgorithmException tlsv11Exception) {
|
||||
Timber.w("TLSv1.1 is not supported in this device; falling through TLSv1.0");
|
||||
return SSLContext.getInstance(TlsVersion.TLS_1_0.javaName());
|
||||
// should be available in any device; see reference of supported protocols in
|
||||
// http://developer.android.com/reference/javax/net/ssl/SSLSocket.html
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private OkHttpClient buildNewOkHttpClient(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager,
|
||||
CookieJar cookieJar) {
|
||||
return new OkHttpClient.Builder()
|
||||
.addNetworkInterceptor(getLogInterceptor())
|
||||
.addNetworkInterceptor(DebugInterceptorFactory.INSTANCE.getInterceptor())
|
||||
.protocols(Collections.singletonList(Protocol.HTTP_1_1))
|
||||
.readTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.connectTimeout(HttpConstants.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
.followRedirects(false)
|
||||
.sslSocketFactory(sslSocketFactory, trustManager)
|
||||
.hostnameVerifier((asdf, usdf) -> true)
|
||||
.cookieJar(cookieJar)
|
||||
.build();
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
public LogInterceptor getLogInterceptor() {
|
||||
return mLogInterceptor;
|
||||
}
|
||||
|
||||
public List<Cookie> getCookiesFromUrl(HttpUrl httpUrl) {
|
||||
return mCookieStore.get(httpUrl.host());
|
||||
}
|
||||
|
||||
public void clearCookies() {
|
||||
mCookieStore.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,222 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.http;
|
||||
|
||||
/**
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
public class HttpConstants {
|
||||
|
||||
/***********************************************************************************************************
|
||||
*************************************************** HEADERS ***********************************************
|
||||
***********************************************************************************************************/
|
||||
|
||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
public static final String COOKIE_HEADER = "Cookie";
|
||||
public static final String BEARER_AUTHORIZATION_KEY = "Bearer ";
|
||||
public static final String USER_AGENT_HEADER = "User-Agent";
|
||||
public static final String IF_MATCH_HEADER = "If-Match";
|
||||
public static final String IF_NONE_MATCH_HEADER = "If-None-Match";
|
||||
public static final String CONTENT_TYPE_HEADER = "Content-Type";
|
||||
public static final String ACCEPT_LANGUAGE_HEADER = "Accept-Language";
|
||||
public static final String CONTENT_LENGTH_HEADER = "Content-Length";
|
||||
public static final String OC_TOTAL_LENGTH_HEADER = "OC-Total-Length";
|
||||
public static final String OC_X_OC_MTIME_HEADER = "X-OC-Mtime";
|
||||
public static final String OC_X_REQUEST_ID = "X-Request-ID";
|
||||
public static final String LOCATION_HEADER = "Location";
|
||||
public static final String LOCATION_HEADER_LOWER = "location";
|
||||
public static final String CONTENT_TYPE_URLENCODED_UTF8 = "application/x-www-form-urlencoded; charset=utf-8";
|
||||
public static final String ACCEPT_ENCODING_HEADER = "Accept-Encoding";
|
||||
public static final String ACCEPT_ENCODING_IDENTITY = "identity";
|
||||
public static final String OC_FILE_REMOTE_ID = "OC-FileId";
|
||||
|
||||
// OAuth
|
||||
public static final String OAUTH_HEADER_AUTHORIZATION_CODE = "code";
|
||||
public static final String OAUTH_HEADER_GRANT_TYPE = "grant_type";
|
||||
public static final String OAUTH_HEADER_REDIRECT_URI = "redirect_uri";
|
||||
public static final String OAUTH_HEADER_REFRESH_TOKEN = "refresh_token";
|
||||
public static final String OAUTH_HEADER_CODE_VERIFIER = "code_verifier";
|
||||
|
||||
/***********************************************************************************************************
|
||||
************************************************ CONTENT TYPES ********************************************
|
||||
***********************************************************************************************************/
|
||||
|
||||
public static final String CONTENT_TYPE_XML = "application/xml";
|
||||
public static final String CONTENT_TYPE_JSON = "application/json";
|
||||
public static final String CONTENT_TYPE_WWW_FORM = "application/x-www-form-urlencoded";
|
||||
|
||||
/***********************************************************************************************************
|
||||
************************************************ ARGUMENTS NAMES ********************************************
|
||||
***********************************************************************************************************/
|
||||
|
||||
public static final String PARAM_FORMAT = "format";
|
||||
|
||||
/***********************************************************************************************************
|
||||
************************************************ ARGUMENTS VALUES ********************************************
|
||||
***********************************************************************************************************/
|
||||
|
||||
public static final String VALUE_FORMAT = "json";
|
||||
|
||||
/***********************************************************************************************************
|
||||
************************************************ STATUS CODES *********************************************
|
||||
***********************************************************************************************************/
|
||||
|
||||
/**
|
||||
* 1xx Informational
|
||||
*/
|
||||
|
||||
// 100 Continue (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_CONTINUE = 100;
|
||||
// 101 Switching Protocols (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_SWITCHING_PROTOCOLS = 101;
|
||||
// 102 Processing (WebDAV - RFC 2518)
|
||||
public static final int HTTP_PROCESSING = 102;
|
||||
|
||||
/**
|
||||
* 2xx Success
|
||||
*/
|
||||
|
||||
// 200 OK (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_OK = 200;
|
||||
// 201 Created (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_CREATED = 201;
|
||||
// 202 Accepted (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_ACCEPTED = 202;
|
||||
// 203 Non Authoritative Information (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
|
||||
// 204 No Content</tt> (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_NO_CONTENT = 204;
|
||||
// 205 Reset Content</tt> (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_RESET_CONTENT = 205;
|
||||
// 206 Partial Content</tt> (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_PARTIAL_CONTENT = 206;
|
||||
//207 Multi-Status (WebDAV - RFC 2518) or 207 Partial Update OK (HTTP/1.1 - draft-ietf-http-v11-spec-rev-01?)
|
||||
public static final int HTTP_MULTI_STATUS = 207;
|
||||
|
||||
/**
|
||||
* 3xx Redirection
|
||||
*/
|
||||
|
||||
// 300 Mutliple Choices</tt> (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_MULTIPLE_CHOICES = 300;
|
||||
// 301 Moved Permanently</tt> (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_MOVED_PERMANENTLY = 301;
|
||||
// 302 Moved Temporarily</tt> (Sometimes <tt>Found) (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_MOVED_TEMPORARILY = 302;
|
||||
// 303 See Other (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_SEE_OTHER = 303;
|
||||
// 304 Not Modified (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_NOT_MODIFIED = 304;
|
||||
// 305 Use Proxy (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_USE_PROXY = 305;
|
||||
// 307 Temporary Redirect (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_TEMPORARY_REDIRECT = 307;
|
||||
|
||||
/**
|
||||
* 4xx Client Error
|
||||
*/
|
||||
|
||||
// 400 Bad Request (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_BAD_REQUEST = 400;
|
||||
// 401 Unauthorized (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_UNAUTHORIZED = 401;
|
||||
// 402 Payment Required (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_PAYMENT_REQUIRED = 402;
|
||||
// 403 Forbidden (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_FORBIDDEN = 403;
|
||||
// 404 Not Found (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_NOT_FOUND = 404;
|
||||
// 405 Method Not Allowed (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_METHOD_NOT_ALLOWED = 405;
|
||||
// 406 Not Acceptable (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_NOT_ACCEPTABLE = 406;
|
||||
// 407 Proxy Authentication Required (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
|
||||
// 408 Request Timeout (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_REQUEST_TIMEOUT = 408;
|
||||
// 409 Conflict (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_CONFLICT = 409;
|
||||
// 410 Gone (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_GONE = 410;
|
||||
// 411 Length Required (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_LENGTH_REQUIRED = 411;
|
||||
// 412 Precondition Failed (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_PRECONDITION_FAILED = 412;
|
||||
// 413 Request Entity Too Large (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_REQUEST_TOO_LONG = 413;
|
||||
// 414 Request-URI Too Long (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_REQUEST_URI_TOO_LONG = 414;
|
||||
// 415 Unsupported Media Type (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
|
||||
// 416 Requested Range Not Satisfiable (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
|
||||
// 417 Expectation Failed (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_EXPECTATION_FAILED = 417;
|
||||
// 419 Insufficient Space on Resource (WebDAV - draft-ietf-webdav-protocol-05?)
|
||||
// or <tt>419 Proxy Reauthentication Required (HTTP/1.1 drafts?)
|
||||
public static final int HTTP_INSUFFICIENT_SPACE_ON_RESOURCE = 419;
|
||||
// 420 Method Failure (WebDAV - draft-ietf-webdav-protocol-05?)
|
||||
public static final int HTTP_METHOD_FAILURE = 420;
|
||||
// 422 Unprocessable Entity (WebDAV - RFC 2518)
|
||||
public static final int HTTP_UNPROCESSABLE_ENTITY = 422;
|
||||
// 423 Locked (WebDAV - RFC 2518)
|
||||
public static final int HTTP_LOCKED = 423;
|
||||
// 424 Failed Dependency (WebDAV - RFC 2518)
|
||||
public static final int HTTP_FAILED_DEPENDENCY = 424;
|
||||
public static final int HTTP_TOO_EARLY = 425;
|
||||
|
||||
/**
|
||||
* 5xx Client Error
|
||||
*/
|
||||
|
||||
// 500 Server Error (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_INTERNAL_SERVER_ERROR = 500;
|
||||
// 501 Not Implemented (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_NOT_IMPLEMENTED = 501;
|
||||
// 502 Bad Gateway (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_BAD_GATEWAY = 502;
|
||||
// 503 Service Unavailable (HTTP/1.0 - RFC 1945)
|
||||
public static final int HTTP_SERVICE_UNAVAILABLE = 503;
|
||||
// 504 Gateway Timeout (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_GATEWAY_TIMEOUT = 504;
|
||||
// 505 HTTP Version Not Supported (HTTP/1.1 - RFC 2616)
|
||||
public static final int HTTP_HTTP_VERSION_NOT_SUPPORTED = 505;
|
||||
// 507 Insufficient Storage (WebDAV - RFC 2518)
|
||||
public static final int HTTP_INSUFFICIENT_STORAGE = 507;
|
||||
|
||||
/***********************************************************************************************************
|
||||
*************************************************** TIMEOUTS **********************************************
|
||||
***********************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Default timeout for waiting data from the server
|
||||
*/
|
||||
public static final int DEFAULT_DATA_TIMEOUT = 60000;
|
||||
|
||||
/**
|
||||
* Default timeout for establishing a connection
|
||||
*/
|
||||
public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http
|
||||
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_JSON
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_WWW_FORM
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.CONTENT_TYPE_XML
|
||||
import okhttp3.MediaType
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
||||
object LogBuilder {
|
||||
fun logHttp(
|
||||
networkPetition: NetworkPetition,
|
||||
networkNode: NetworkNode,
|
||||
requestId: String? = "",
|
||||
description: String
|
||||
) = Timber.d("[Network, $networkPetition] [$networkNode] [$requestId] $description")
|
||||
}
|
||||
|
||||
enum class NetworkPetition {
|
||||
REQUEST, RESPONSE;
|
||||
|
||||
override fun toString(): String = super.toString().lowercase(Locale.ROOT)
|
||||
}
|
||||
|
||||
enum class NetworkNode {
|
||||
INFO, HEADER, BODY;
|
||||
|
||||
override fun toString(): String = super.toString().lowercase(Locale.ROOT)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a media type is loggable.
|
||||
*
|
||||
* @return true if its type is text, xml, json, or x-www-form-urlencoded.
|
||||
*/
|
||||
fun MediaType?.isLoggable(): Boolean =
|
||||
this?.let { mediaType ->
|
||||
val mediaTypeString = mediaType.toString()
|
||||
(mediaType.type == "text" ||
|
||||
mediaTypeString.contains(CONTENT_TYPE_XML) ||
|
||||
mediaTypeString.contains(CONTENT_TYPE_JSON) ||
|
||||
mediaTypeString.contains(CONTENT_TYPE_WWW_FORM))
|
||||
} ?: false
|
@ -0,0 +1,179 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http
|
||||
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.AUTHORIZATION_HEADER
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.OC_X_REQUEST_ID
|
||||
import com.owncloud.android.lib.common.http.LogBuilder.logHttp
|
||||
import com.owncloud.android.lib.common.http.NetworkNode.BODY
|
||||
import com.owncloud.android.lib.common.http.NetworkNode.HEADER
|
||||
import com.owncloud.android.lib.common.http.NetworkNode.INFO
|
||||
import com.owncloud.android.lib.common.http.NetworkPetition.REQUEST
|
||||
import com.owncloud.android.lib.common.http.NetworkPetition.RESPONSE
|
||||
import okhttp3.Headers
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody
|
||||
import okio.Buffer
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets
|
||||
import kotlin.math.max
|
||||
|
||||
class LogInterceptor : Interceptor {
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
|
||||
if (!httpLogsEnabled) {
|
||||
return chain.proceed(chain.request())
|
||||
}
|
||||
|
||||
val request = chain.request().also {
|
||||
val requestId = it.headers[OC_X_REQUEST_ID]
|
||||
logHttp(REQUEST, INFO, requestId, "Method: ${it.method} URL: ${it.url}")
|
||||
logHeaders(requestId, it.headers, REQUEST)
|
||||
logRequestBody(requestId, it.body)
|
||||
}
|
||||
|
||||
val response = chain.proceed(request)
|
||||
|
||||
return response.also {
|
||||
val requestId = it.request.headers[OC_X_REQUEST_ID]
|
||||
logHttp(
|
||||
RESPONSE,
|
||||
INFO,
|
||||
requestId,
|
||||
"Method: ${request.method} URL: ${request.url} Code: ${it.code} Message: ${it.message}"
|
||||
)
|
||||
logHeaders(requestId, it.headers, RESPONSE)
|
||||
logResponseBody(requestId, it.body)
|
||||
}
|
||||
}
|
||||
|
||||
private fun logHeaders(requestId: String?, headers: Headers, networkPetition: NetworkPetition) {
|
||||
headers.forEach { header ->
|
||||
val headerValue: String = if (header.first.equals(AUTHORIZATION_HEADER, true)) {
|
||||
"[redacted]"
|
||||
} else {
|
||||
header.second
|
||||
}
|
||||
logHttp(networkPetition, HEADER, requestId, "${header.first}: $headerValue")
|
||||
}
|
||||
}
|
||||
|
||||
private fun logRequestBody(requestId: String?, requestBodyParam: RequestBody?) {
|
||||
requestBodyParam?.let { requestBody ->
|
||||
|
||||
if (requestBody.isOneShot()) {
|
||||
logHttp(REQUEST, BODY, requestId, "One shot body -- Omitted")
|
||||
return@let
|
||||
}
|
||||
|
||||
if (requestBody.isDuplex()) {
|
||||
logHttp(REQUEST, BODY, requestId, "Duplex body -- Omitted")
|
||||
return@let
|
||||
}
|
||||
|
||||
val buffer = Buffer()
|
||||
requestBody.writeTo(buffer)
|
||||
|
||||
val contentType = requestBody.contentType()
|
||||
val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8
|
||||
|
||||
logHttp(REQUEST, BODY, requestId, "Length: ${requestBody.contentLength()} byte body")
|
||||
logHttp(REQUEST, BODY, requestId, "Type: ${requestBody.contentType()}")
|
||||
logHttp(REQUEST, BODY, requestId, "--> Body start for request")
|
||||
|
||||
if (contentType.isLoggable()) {
|
||||
if (requestBody.contentLength() < LIMIT_BODY_LOG) {
|
||||
logHttp(REQUEST, BODY, requestId, buffer.readString(charset))
|
||||
} else {
|
||||
logHttp(REQUEST, BODY, requestId, buffer.readString(LIMIT_BODY_LOG, charset))
|
||||
}
|
||||
logHttp(
|
||||
REQUEST,
|
||||
BODY,
|
||||
requestId,
|
||||
"<-- Body end for request -- Omitted: ${max(0, requestBody.contentLength() - LIMIT_BODY_LOG)} bytes"
|
||||
)
|
||||
} else {
|
||||
logHttp(
|
||||
REQUEST,
|
||||
BODY,
|
||||
requestId,
|
||||
"<-- Body end for request -- Binary -- Omitted: ${requestBody.contentLength()} bytes"
|
||||
)
|
||||
}
|
||||
|
||||
} ?: logHttp(REQUEST, BODY, requestId, "Empty body")
|
||||
}
|
||||
|
||||
private fun logResponseBody(requestId: String?, responseBodyParam: ResponseBody?) {
|
||||
responseBodyParam?.let { responseBody ->
|
||||
|
||||
val contentType = responseBody.contentType()
|
||||
val charset: Charset = contentType?.charset(StandardCharsets.UTF_8) ?: StandardCharsets.UTF_8
|
||||
|
||||
logHttp(RESPONSE, BODY, requestId, "Length: ${responseBody.contentLength()} byte body")
|
||||
logHttp(RESPONSE, BODY, requestId, "Type: ${responseBody.contentType()}")
|
||||
logHttp(RESPONSE, BODY, requestId, "--> Body start for response")
|
||||
|
||||
val source = responseBody.source()
|
||||
source.request(LIMIT_BODY_LOG)
|
||||
val buffer = source.buffer
|
||||
|
||||
if (contentType.isLoggable()) {
|
||||
|
||||
if (responseBody.contentLength() < LIMIT_BODY_LOG) {
|
||||
logHttp(RESPONSE, BODY, requestId, buffer.clone().readString(charset))
|
||||
} else {
|
||||
logHttp(RESPONSE, BODY, requestId, buffer.clone().readString(LIMIT_BODY_LOG, charset))
|
||||
}
|
||||
logHttp(
|
||||
RESPONSE,
|
||||
BODY,
|
||||
requestId,
|
||||
"<-- Body end for response -- Omitted: ${
|
||||
max(
|
||||
0,
|
||||
responseBody.contentLength() - LIMIT_BODY_LOG
|
||||
)
|
||||
} bytes"
|
||||
)
|
||||
} else {
|
||||
logHttp(
|
||||
RESPONSE,
|
||||
BODY,
|
||||
requestId,
|
||||
"<-- Body end for response -- Binary -- Omitted: ${responseBody.contentLength()} bytes"
|
||||
)
|
||||
}
|
||||
} ?: logHttp(RESPONSE, BODY, requestId, "Empty body")
|
||||
}
|
||||
|
||||
companion object {
|
||||
var httpLogsEnabled: Boolean = false
|
||||
private const val LIMIT_BODY_LOG: Long = 1024
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.http;
|
||||
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
public class TLSSocketFactory extends SSLSocketFactory {
|
||||
private SSLSocketFactory mInternalSSLSocketFactory;
|
||||
|
||||
public TLSSocketFactory(SSLSocketFactory delegate) {
|
||||
mInternalSSLSocketFactory = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return mInternalSSLSocketFactory.getDefaultCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return mInternalSSLSocketFactory.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
|
||||
return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(s, host, port, autoClose));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
|
||||
return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(host, port, localHost, localPort));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress host, int port) throws IOException {
|
||||
return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws
|
||||
IOException {
|
||||
return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
|
||||
}
|
||||
|
||||
private Socket enableTLSOnSocket(Socket socket) {
|
||||
if((socket instanceof SSLSocket)) {
|
||||
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2", "TLSv1.3"});
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.http.methods
|
||||
|
||||
import com.owncloud.android.lib.common.http.HttpClient
|
||||
import okhttp3.Call
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import java.io.InputStream
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
abstract class HttpBaseMethod constructor(url: URL) {
|
||||
var httpUrl: HttpUrl = url.toHttpUrlOrNull() ?: throw MalformedURLException()
|
||||
var request: Request
|
||||
var followPermanentRedirects = false
|
||||
abstract var response: Response
|
||||
var call: Call? = null
|
||||
|
||||
var followRedirects: Boolean = true
|
||||
var retryOnConnectionFailure: Boolean = true
|
||||
var connectionTimeoutVal: Long? = null
|
||||
var connectionTimeoutUnit: TimeUnit? = null
|
||||
var readTimeoutVal: Long? = null
|
||||
private set
|
||||
var readTimeoutUnit: TimeUnit? = null
|
||||
private set
|
||||
|
||||
init {
|
||||
request = Request.Builder()
|
||||
.url(httpUrl)
|
||||
.build()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
open fun execute(httpClient: HttpClient): Int {
|
||||
val okHttpClient = httpClient.okHttpClient.newBuilder().apply {
|
||||
retryOnConnectionFailure(retryOnConnectionFailure)
|
||||
followRedirects(followRedirects)
|
||||
readTimeoutUnit?.let { unit ->
|
||||
readTimeoutVal?.let { readTimeout(it, unit) }
|
||||
}
|
||||
connectionTimeoutUnit?.let { unit ->
|
||||
connectionTimeoutVal?.let { connectTimeout(it, unit) }
|
||||
}
|
||||
}.build()
|
||||
|
||||
return onExecute(okHttpClient)
|
||||
}
|
||||
|
||||
open fun setUrl(url: HttpUrl) {
|
||||
request = request.newBuilder()
|
||||
.url(url)
|
||||
.build()
|
||||
}
|
||||
|
||||
/****************
|
||||
*** Requests ***
|
||||
****************/
|
||||
|
||||
fun getRequestHeader(name: String): String? {
|
||||
return request.header(name)
|
||||
}
|
||||
|
||||
fun getRequestHeadersAsHashMap(): HashMap<String, String?> {
|
||||
val headers: HashMap<String, String?> = HashMap()
|
||||
val superHeaders: Set<String> = request.headers.names()
|
||||
superHeaders.forEach {
|
||||
headers[it] = getRequestHeader(it)
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
open fun addRequestHeader(name: String, value: String) {
|
||||
request = request.newBuilder()
|
||||
.addHeader(name, value)
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a header and replace it if already exists with that name
|
||||
*
|
||||
* @param name header name
|
||||
* @param value header value
|
||||
*/
|
||||
open fun setRequestHeader(name: String, value: String) {
|
||||
request = request.newBuilder()
|
||||
.header(name, value)
|
||||
.build()
|
||||
}
|
||||
|
||||
/****************
|
||||
*** Response ***
|
||||
****************/
|
||||
val statusCode: Int
|
||||
get() = response.code
|
||||
|
||||
val statusMessage: String
|
||||
get() = response.message
|
||||
|
||||
// Headers
|
||||
open fun getResponseHeaders(): Headers? {
|
||||
return response.headers
|
||||
}
|
||||
|
||||
open fun getResponseHeader(headerName: String): String? {
|
||||
return response.header(headerName)
|
||||
}
|
||||
|
||||
// Body
|
||||
fun getResponseBodyAsString(): String? = response.body?.string()
|
||||
|
||||
open fun getResponseBodyAsStream(): InputStream? {
|
||||
return response.body?.byteStream()
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the final url after following the last redirect.
|
||||
*/
|
||||
open fun getFinalUrl() = response.request.url
|
||||
|
||||
/*************************
|
||||
*** Connection Params ***
|
||||
*************************/
|
||||
|
||||
//////////////////////////////
|
||||
// Setter
|
||||
//////////////////////////////
|
||||
// Connection parameters
|
||||
|
||||
|
||||
open fun setReadTimeout(readTimeout: Long, timeUnit: TimeUnit) {
|
||||
readTimeoutVal = readTimeout
|
||||
readTimeoutUnit = timeUnit
|
||||
}
|
||||
|
||||
open fun setConnectionTimeout(
|
||||
connectionTimeout: Long,
|
||||
timeUnit: TimeUnit
|
||||
) {
|
||||
connectionTimeoutVal = connectionTimeout
|
||||
connectionTimeoutUnit = timeUnit
|
||||
}
|
||||
|
||||
/************
|
||||
*** Call ***
|
||||
************/
|
||||
open fun abort() {
|
||||
call?.cancel()
|
||||
}
|
||||
|
||||
open val isAborted: Boolean
|
||||
get() = call?.isCanceled() ?: false
|
||||
|
||||
//////////////////////////////
|
||||
// For override
|
||||
//////////////////////////////
|
||||
@Throws(Exception::class)
|
||||
protected abstract fun onExecute(okHttpClient: OkHttpClient): Int
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -21,33 +21,23 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
||||
|
||||
package com.owncloud.android.lib.resources.shares;
|
||||
|
||||
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
|
||||
import okhttp3.OkHttpClient
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Contains Constants for Share Operation
|
||||
*
|
||||
* @author masensio
|
||||
* OkHttp delete calls wrapper
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
|
||||
public class ShareUtils {
|
||||
|
||||
// OCS Route
|
||||
public static final String SHARING_API_PATH ="/ocs/v1.php/apps/files_sharing/api/v1/shares";
|
||||
|
||||
// String to build the link with the token of a share:
|
||||
public static final String SHARING_LINK_PATH_BEFORE_VERSION_8 = "/public.php?service=files&t=";
|
||||
public static final String SHARING_LINK_PATH_AFTER_VERSION_8 = "/index.php/s/";
|
||||
|
||||
public static String getSharingLinkPath(OwnCloudVersion version){
|
||||
if (version!= null && version.isAfter8Version()){
|
||||
return SHARING_LINK_PATH_AFTER_VERSION_8;
|
||||
} else {
|
||||
return SHARING_LINK_PATH_BEFORE_VERSION_8;
|
||||
class DeleteMethod(url: URL) : HttpMethod(url) {
|
||||
@Throws(IOException::class)
|
||||
override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||
request = request.newBuilder()
|
||||
.delete()
|
||||
.build()
|
||||
return super.onExecute(okHttpClient)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* OkHttp get calls wrapper
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
class GetMethod(url: URL) : HttpMethod(url) {
|
||||
@Throws(IOException::class)
|
||||
override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||
request = request.newBuilder()
|
||||
.get()
|
||||
.build()
|
||||
return super.onExecute(okHttpClient)
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -21,26 +21,27 @@
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.sampleclient;
|
||||
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.TextView;
|
||||
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Response
|
||||
import java.net.URL
|
||||
|
||||
import com.owncloud.android.lib.resources.files.RemoteFile;
|
||||
/**
|
||||
* Wrapper to perform OkHttp calls
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
abstract class HttpMethod(
|
||||
url: URL
|
||||
) : HttpBaseMethod(url) {
|
||||
|
||||
public class FilesArrayAdapter extends ArrayAdapter<RemoteFile> {
|
||||
override lateinit var response: Response
|
||||
|
||||
public FilesArrayAdapter(Context context, int resource) {
|
||||
super(context, resource);
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
TextView textView = (TextView)super.getView(position, convertView, parent);
|
||||
textView.setText(getItem(position).getRemotePath());
|
||||
return textView;
|
||||
public override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||
call = okHttpClient.newCall(request)
|
||||
call?.let { response = it.execute() }
|
||||
return super.statusCode
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.RequestBody
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* OkHttp post calls wrapper
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
class PostMethod(
|
||||
url: URL,
|
||||
private val postRequestBody: RequestBody
|
||||
) : HttpMethod(url) {
|
||||
@Throws(IOException::class)
|
||||
override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||
request = request.newBuilder()
|
||||
.post(postRequestBody)
|
||||
.build()
|
||||
return super.onExecute(okHttpClient)
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.nonwebdav
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.RequestBody
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* OkHttp put calls wrapper
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
class PutMethod(
|
||||
url: URL,
|
||||
private val putRequestBody: RequestBody
|
||||
) : HttpMethod(url) {
|
||||
@Throws(IOException::class)
|
||||
override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||
request = request.newBuilder()
|
||||
.put(putRequestBody)
|
||||
.build()
|
||||
return super.onExecute(okHttpClient)
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.webdav
|
||||
|
||||
import at.bitfire.dav4jvm.DavOCResource
|
||||
import okhttp3.Response
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Copy calls wrapper
|
||||
*
|
||||
* @author Christian Schabesberger
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
class CopyMethod(
|
||||
val url: URL,
|
||||
private val destinationUrl: String,
|
||||
val forceOverride: Boolean = false
|
||||
) : DavMethod(url) {
|
||||
@Throws(Exception::class)
|
||||
public override fun onDavExecute(davResource: DavOCResource): Int {
|
||||
davResource.copy(
|
||||
destinationUrl,
|
||||
forceOverride,
|
||||
super.getRequestHeadersAsHashMap()
|
||||
) { callBackResponse: Response ->
|
||||
response = callBackResponse
|
||||
}
|
||||
return super.statusCode
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.webdav
|
||||
|
||||
/**
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
object DavConstants {
|
||||
const val DEPTH_0 = 0
|
||||
const val DEPTH_1 = 1
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.webdav
|
||||
|
||||
import at.bitfire.dav4jvm.Dav4jvm.log
|
||||
import at.bitfire.dav4jvm.DavOCResource
|
||||
import at.bitfire.dav4jvm.exception.HttpException
|
||||
import at.bitfire.dav4jvm.exception.RedirectException
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Protocol
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Wrapper to perform WebDAV (dav4android) calls
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
abstract class DavMethod protected constructor(url: URL) : HttpBaseMethod(url) {
|
||||
override lateinit var response: Response
|
||||
private var davResource: DavOCResource? = null
|
||||
|
||||
override fun abort() {
|
||||
davResource?.cancelCall()
|
||||
}
|
||||
|
||||
protected abstract fun onDavExecute(davResource: DavOCResource): Int
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun onExecute(okHttpClient: OkHttpClient): Int {
|
||||
return try {
|
||||
davResource = DavOCResource(
|
||||
okHttpClient.newBuilder().followRedirects(false).build(),
|
||||
httpUrl,
|
||||
log
|
||||
)
|
||||
|
||||
onDavExecute(davResource!!)
|
||||
} catch (httpException: HttpException) {
|
||||
// Modify responses with information gathered from exceptions
|
||||
if (httpException is RedirectException) {
|
||||
response = Response.Builder()
|
||||
.header(
|
||||
HttpConstants.LOCATION_HEADER, httpException.redirectLocation
|
||||
)
|
||||
.code(httpException.code)
|
||||
.request(request)
|
||||
.message(httpException.message ?: "")
|
||||
.protocol(Protocol.HTTP_1_1)
|
||||
.build()
|
||||
} else {
|
||||
// The check below should be included in okhttp library, method ResponseBody.create(
|
||||
// TODO check most recent versions of okhttp to see if this is already fixed and try to update if so
|
||||
if (response.body?.contentType() != null) {
|
||||
val responseBody = (httpException.responseBody ?: "").toResponseBody(response.body?.contentType())
|
||||
response = response.newBuilder()
|
||||
.body(responseBody)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
httpException.code
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Getter
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
override val isAborted: Boolean
|
||||
get() = davResource?.isCallAborted() ?: false
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.webdav
|
||||
|
||||
import at.bitfire.dav4jvm.Property
|
||||
import at.bitfire.dav4jvm.PropertyUtils.getQuotaPropset
|
||||
import at.bitfire.dav4jvm.property.CreationDate
|
||||
import at.bitfire.dav4jvm.property.DisplayName
|
||||
import at.bitfire.dav4jvm.property.GetContentLength
|
||||
import at.bitfire.dav4jvm.property.GetContentType
|
||||
import at.bitfire.dav4jvm.property.GetETag
|
||||
import at.bitfire.dav4jvm.property.GetLastModified
|
||||
import at.bitfire.dav4jvm.property.OCId
|
||||
import at.bitfire.dav4jvm.property.OCPermissions
|
||||
import at.bitfire.dav4jvm.property.OCPrivatelink
|
||||
import at.bitfire.dav4jvm.property.OCSize
|
||||
import at.bitfire.dav4jvm.property.ResourceType
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
|
||||
|
||||
object DavUtils {
|
||||
@JvmStatic val allPropSet: Array<Property.Name>
|
||||
get() = arrayOf(
|
||||
DisplayName.NAME,
|
||||
GetContentType.NAME,
|
||||
ResourceType.NAME,
|
||||
GetContentLength.NAME,
|
||||
GetLastModified.NAME,
|
||||
CreationDate.NAME,
|
||||
GetETag.NAME,
|
||||
OCPermissions.NAME,
|
||||
OCId.NAME,
|
||||
OCSize.NAME,
|
||||
OCPrivatelink.NAME,
|
||||
OCShareTypes.NAME,
|
||||
)
|
||||
|
||||
val quotaPropSet: Array<Property.Name>
|
||||
get() = getQuotaPropset()
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.webdav
|
||||
|
||||
import at.bitfire.dav4jvm.DavOCResource
|
||||
import okhttp3.Response
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* MkCol calls wrapper
|
||||
*
|
||||
* @author Christian Schabesberger
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
class MkColMethod(url: URL) : DavMethod(url) {
|
||||
@Throws(Exception::class)
|
||||
public override fun onDavExecute(davResource: DavOCResource): Int {
|
||||
davResource.mkCol(
|
||||
xmlBody = null,
|
||||
listOfHeaders = super.getRequestHeadersAsHashMap()
|
||||
) { callBackResponse: Response ->
|
||||
response = callBackResponse
|
||||
}
|
||||
return super.statusCode
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.webdav
|
||||
|
||||
import at.bitfire.dav4jvm.DavOCResource
|
||||
import okhttp3.Response
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Move calls wrapper
|
||||
*
|
||||
* @author Christian Schabesberger
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
class MoveMethod(
|
||||
url: URL,
|
||||
private val destinationUrl: String,
|
||||
val forceOverride: Boolean = false
|
||||
) : DavMethod(url) {
|
||||
@Throws(Exception::class)
|
||||
override fun onDavExecute(davResource: DavOCResource): Int {
|
||||
davResource.move(
|
||||
destinationUrl,
|
||||
forceOverride,
|
||||
super.getRequestHeadersAsHashMap()
|
||||
) { callBackResponse: Response ->
|
||||
response = callBackResponse
|
||||
}
|
||||
return super.statusCode
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.webdav
|
||||
|
||||
import at.bitfire.dav4jvm.DavOCResource
|
||||
import at.bitfire.dav4jvm.Property
|
||||
import at.bitfire.dav4jvm.Response
|
||||
import at.bitfire.dav4jvm.Response.HrefRelation
|
||||
import at.bitfire.dav4jvm.exception.DavException
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Propfind calls wrapper
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
class PropfindMethod(
|
||||
url: URL,
|
||||
private val depth: Int,
|
||||
private val propertiesToRequest: Array<Property.Name>
|
||||
) : DavMethod(url) {
|
||||
|
||||
// response
|
||||
val members: MutableList<Response>
|
||||
var root: Response?
|
||||
private set
|
||||
|
||||
@Throws(IOException::class, DavException::class)
|
||||
public override fun onDavExecute(davResource: DavOCResource): Int {
|
||||
davResource.propfind(
|
||||
depth = depth,
|
||||
reqProp = propertiesToRequest,
|
||||
listOfHeaders = super.getRequestHeadersAsHashMap(),
|
||||
callback = { response: Response, hrefRelation: HrefRelation ->
|
||||
when (hrefRelation) {
|
||||
HrefRelation.MEMBER -> members.add(response)
|
||||
HrefRelation.SELF -> this.root = response
|
||||
HrefRelation.OTHER -> {
|
||||
}
|
||||
}
|
||||
}, rawCallback = { callBackResponse: okhttp3.Response ->
|
||||
response = callBackResponse
|
||||
})
|
||||
return statusCode
|
||||
}
|
||||
|
||||
init {
|
||||
members = arrayListOf()
|
||||
this.root = null
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.webdav
|
||||
|
||||
import at.bitfire.dav4jvm.DavOCResource
|
||||
import at.bitfire.dav4jvm.exception.HttpException
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import okhttp3.RequestBody
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Put calls wrapper
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
class PutMethod(
|
||||
url: URL,
|
||||
private val putRequestBody: RequestBody
|
||||
) : DavMethod(url) {
|
||||
@Throws(IOException::class, HttpException::class)
|
||||
public override fun onDavExecute(davResource: DavOCResource): Int {
|
||||
davResource.put(
|
||||
putRequestBody,
|
||||
super.getRequestHeader(HttpConstants.IF_MATCH_HEADER),
|
||||
getRequestHeadersAsHashMap()
|
||||
) { callBackResponse ->
|
||||
response = callBackResponse
|
||||
}
|
||||
return super.statusCode
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.webdav.properties
|
||||
|
||||
import at.bitfire.dav4jvm.Property
|
||||
import at.bitfire.dav4jvm.XmlUtils
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
|
||||
class OCShareTypes : ShareTypeListProperty() {
|
||||
|
||||
class Factory : ShareTypeListProperty.Factory() {
|
||||
|
||||
override fun create(parser: XmlPullParser) =
|
||||
create(parser, OCShareTypes())
|
||||
|
||||
override fun getName(): Property.Name = NAME
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val NAME = Property.Name(XmlUtils.NS_OWNCLOUD, "share-types")
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.http.methods.webdav.properties
|
||||
|
||||
import at.bitfire.dav4jvm.Property
|
||||
import at.bitfire.dav4jvm.PropertyFactory
|
||||
import at.bitfire.dav4jvm.XmlUtils
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import java.util.LinkedList
|
||||
|
||||
abstract class ShareTypeListProperty : Property {
|
||||
|
||||
val shareTypes = LinkedList<String>()
|
||||
|
||||
override fun toString() = "share types =[" + shareTypes.joinToString(", ") + "]"
|
||||
|
||||
abstract class Factory : PropertyFactory {
|
||||
|
||||
fun create(parser: XmlPullParser, list: ShareTypeListProperty): ShareTypeListProperty {
|
||||
XmlUtils.readTextPropertyList(parser, Property.Name(XmlUtils.NS_OWNCLOUD, "share-type"), list.shareTypes)
|
||||
return list
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -24,69 +24,58 @@
|
||||
|
||||
package com.owncloud.android.lib.common.network;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertPathValidatorException;
|
||||
import java.security.cert.CertStoreException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import timber.log.Timber;
|
||||
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertPathValidatorException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* @author David A. Velasco
|
||||
*/
|
||||
public class AdvancedX509TrustManager implements X509TrustManager {
|
||||
|
||||
private static final String TAG = AdvancedX509TrustManager.class.getSimpleName();
|
||||
|
||||
private X509TrustManager mStandardTrustManager = null;
|
||||
private X509TrustManager mStandardTrustManager;
|
||||
private KeyStore mKnownServersKeyStore;
|
||||
|
||||
/**
|
||||
* Constructor for AdvancedX509TrustManager
|
||||
*
|
||||
* @param knownServersKeyStore Local certificates store with server certificates explicitly trusted by the user.
|
||||
* @throws CertStoreException When no default X509TrustManager instance was found in the system.
|
||||
*/
|
||||
public AdvancedX509TrustManager(KeyStore knownServersKeyStore)
|
||||
throws NoSuchAlgorithmException, KeyStoreException, CertStoreException {
|
||||
public AdvancedX509TrustManager(KeyStore knownServersKeyStore) throws NoSuchAlgorithmException, KeyStoreException {
|
||||
super();
|
||||
TrustManagerFactory factory = TrustManagerFactory
|
||||
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
factory.init((KeyStore) null);
|
||||
mStandardTrustManager = findX509TrustManager(factory);
|
||||
|
||||
mKnownServersKeyStore = knownServersKeyStore;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Locates the first X509TrustManager provided by a given TrustManagerFactory
|
||||
*
|
||||
* @param factory TrustManagerFactory to inspect in the search for a X509TrustManager
|
||||
* @return The first X509TrustManager found in factory.
|
||||
* @throws CertStoreException When no X509TrustManager instance was found in factory
|
||||
*/
|
||||
private X509TrustManager findX509TrustManager(TrustManagerFactory factory) throws CertStoreException {
|
||||
TrustManager tms[] = factory.getTrustManagers();
|
||||
for (int i = 0; i < tms.length; i++) {
|
||||
if (tms[i] instanceof X509TrustManager) {
|
||||
return (X509TrustManager) tms[i];
|
||||
private X509TrustManager findX509TrustManager(TrustManagerFactory factory) {
|
||||
TrustManager[] tms = factory.getTrustManagers();
|
||||
for (TrustManager tm : tms) {
|
||||
if (tm instanceof X509TrustManager) {
|
||||
return (X509TrustManager) tm;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],
|
||||
* String authType)
|
||||
@ -95,12 +84,11 @@ public class AdvancedX509TrustManager implements X509TrustManager {
|
||||
mStandardTrustManager.checkClientTrusted(certificates, authType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],
|
||||
* String authType)
|
||||
*/
|
||||
public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
|
||||
public void checkServerTrusted(X509Certificate[] certificates, String authType) {
|
||||
if (!isKnownServer(certificates[0])) {
|
||||
CertificateCombinedException result = new CertificateCombinedException(certificates[0]);
|
||||
try {
|
||||
@ -121,20 +109,20 @@ public class AdvancedX509TrustManager implements X509TrustManager {
|
||||
previousCause = cause;
|
||||
cause = cause.getCause();
|
||||
}
|
||||
if (cause != null && cause instanceof CertPathValidatorException) {
|
||||
if (cause instanceof CertPathValidatorException) {
|
||||
result.setCertPathValidatorException((CertPathValidatorException) cause);
|
||||
} else {
|
||||
result.setOtherCertificateException(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.isException())
|
||||
if (result.isException()) {
|
||||
throw result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
|
||||
*/
|
||||
@ -142,12 +130,11 @@ public class AdvancedX509TrustManager implements X509TrustManager {
|
||||
return mStandardTrustManager.getAcceptedIssuers();
|
||||
}
|
||||
|
||||
|
||||
public boolean isKnownServer(X509Certificate cert) {
|
||||
try {
|
||||
return (mKnownServersKeyStore.getCertificateAlias(cert) != null);
|
||||
} catch (KeyStoreException e) {
|
||||
Log_OC.d(TAG, "Fail while checking certificate in the known-servers store");
|
||||
Timber.e(e, "Fail while checking certificate in the known-servers store");
|
||||
return false;
|
||||
}
|
||||
}
|
@ -24,26 +24,25 @@
|
||||
|
||||
package com.owncloud.android.lib.common.network;
|
||||
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import java.security.cert.CertPathValidatorException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
|
||||
/**
|
||||
* Exception joining all the problems that {@link AdvancedX509TrustManager} can find in
|
||||
* a certificate chain for a server.
|
||||
*
|
||||
* <p>
|
||||
* This was initially created as an extension of CertificateException, but some
|
||||
* implementations of the SSL socket layer in existing devices are REPLACING the CertificateException
|
||||
* instances thrown by {@link javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], String)}
|
||||
* with SSLPeerUnverifiedException FORGETTING THE CAUSING EXCEPTION instead of wrapping it.
|
||||
*
|
||||
* <p>
|
||||
* Due to this, extending RuntimeException is necessary to get that the CertificateCombinedException
|
||||
* instance reaches {@link AdvancedSslSocketFactory#verifyPeerIdentity}.
|
||||
*
|
||||
* <p>
|
||||
* BE CAREFUL. As a RuntimeException extensions, Java compilers do not require to handle it
|
||||
* in client methods. Be sure to use it only when you know exactly where it will go.
|
||||
*
|
||||
@ -51,7 +50,9 @@ import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
*/
|
||||
public class CertificateCombinedException extends RuntimeException {
|
||||
|
||||
/** Generated - to refresh every time the class changes */
|
||||
/**
|
||||
* Generated - to refresh every time the class changes
|
||||
*/
|
||||
private static final long serialVersionUID = -8875782030758554999L;
|
||||
|
||||
private X509Certificate mServerCert = null;
|
@ -0,0 +1,92 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.network
|
||||
|
||||
import com.owncloud.android.lib.resources.files.chunks.ChunkedUploadFromFileSystemOperation.Companion.CHUNK_SIZE
|
||||
import okhttp3.MediaType
|
||||
import okio.BufferedSink
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.channels.FileChannel
|
||||
|
||||
/**
|
||||
* A Request body that represents a file chunk and include information about the progress when uploading it
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
class ChunkFromFileRequestBody(
|
||||
file: File,
|
||||
contentType: MediaType?,
|
||||
private val channel: FileChannel,
|
||||
private val chunkSize: Long = CHUNK_SIZE
|
||||
) : FileRequestBody(file, contentType) {
|
||||
|
||||
private var offset: Long = 0
|
||||
private var alreadyTransferred: Long = 0
|
||||
private val buffer = ByteBuffer.allocate(4_096)
|
||||
|
||||
init {
|
||||
require(chunkSize > 0) { "Chunk size must be greater than zero" }
|
||||
}
|
||||
|
||||
override fun contentLength(): Long {
|
||||
return chunkSize.coerceAtMost(channel.size() - channel.position())
|
||||
}
|
||||
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
var readCount: Int
|
||||
var iterator: Iterator<OnDatatransferProgressListener>
|
||||
try {
|
||||
channel.position(offset)
|
||||
|
||||
val maxCount = (offset + chunkSize).coerceAtMost(channel.size())
|
||||
while (channel.position() < maxCount) {
|
||||
readCount = channel.read(buffer)
|
||||
val bytesToWriteInBuffer = readCount.toLong().coerceAtMost(file.length() - alreadyTransferred).toInt()
|
||||
sink.buffer.write(buffer.array(), 0, bytesToWriteInBuffer)
|
||||
sink.flush()
|
||||
buffer.clear()
|
||||
|
||||
if (alreadyTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks
|
||||
alreadyTransferred += readCount.toLong()
|
||||
}
|
||||
|
||||
synchronized(dataTransferListeners) {
|
||||
iterator = dataTransferListeners.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next().onTransferProgress(readCount.toLong(), alreadyTransferred, file.length(), file.absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Timber.e(exception, "Transferred " + alreadyTransferred + " bytes from a total of " + file.length())
|
||||
}
|
||||
}
|
||||
|
||||
fun setOffset(newOffset: Long) {
|
||||
offset = newOffset
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.network
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.RequestBody
|
||||
import okio.BufferedSink
|
||||
import okio.Source
|
||||
import okio.source
|
||||
import timber.log.Timber
|
||||
import java.io.IOException
|
||||
|
||||
class ContentUriRequestBody(
|
||||
private val contentResolver: ContentResolver,
|
||||
private val contentUri: Uri
|
||||
) : RequestBody(), ProgressiveDataTransferer {
|
||||
|
||||
private val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
|
||||
|
||||
val fileSize: Long = contentResolver.query(contentUri, null, null, null, null)?.use { cursor ->
|
||||
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||
cursor.moveToFirst()
|
||||
cursor.getLong(sizeIndex)
|
||||
} ?: -1
|
||||
|
||||
override fun contentType(): MediaType? {
|
||||
val contentType = contentResolver.getType(contentUri) ?: return null
|
||||
return contentType.toMediaTypeOrNull()
|
||||
}
|
||||
|
||||
override fun contentLength(): Long {
|
||||
return fileSize
|
||||
}
|
||||
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
val inputStream = contentResolver.openInputStream(contentUri)
|
||||
?: throw IOException("Couldn't open content URI for reading: $contentUri")
|
||||
|
||||
val previousTime = System.currentTimeMillis()
|
||||
|
||||
sink.writeAndUpdateProgress(inputStream.source())
|
||||
inputStream.source().close()
|
||||
|
||||
val laterTime = System.currentTimeMillis()
|
||||
|
||||
Timber.d("Difference - ${laterTime - previousTime} milliseconds")
|
||||
}
|
||||
|
||||
private fun BufferedSink.writeAndUpdateProgress(source: Source) {
|
||||
var iterator: Iterator<OnDatatransferProgressListener>
|
||||
|
||||
try {
|
||||
var totalBytesRead = 0L
|
||||
var read: Long
|
||||
while (source.read(this.buffer, BYTES_TO_READ).also { read = it } != -1L) {
|
||||
totalBytesRead += read
|
||||
this.flush()
|
||||
synchronized(dataTransferListeners) {
|
||||
iterator = dataTransferListeners.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next().onTransferProgress(read, totalBytesRead, fileSize, contentUri.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||
synchronized(dataTransferListeners) {
|
||||
dataTransferListeners.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addDatatransferProgressListeners(listeners: MutableCollection<OnDatatransferProgressListener>) {
|
||||
synchronized(dataTransferListeners) {
|
||||
dataTransferListeners.addAll(listeners)
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||
synchronized(dataTransferListeners) {
|
||||
dataTransferListeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BYTES_TO_READ = 4_096L
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.network
|
||||
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.RequestBody
|
||||
import okio.BufferedSink
|
||||
import okio.Source
|
||||
import okio.source
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.HashSet
|
||||
|
||||
/**
|
||||
* A Request body that represents a file and include information about the progress when uploading it
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
open class FileRequestBody(
|
||||
val file: File,
|
||||
private val contentType: MediaType?,
|
||||
) : RequestBody(), ProgressiveDataTransferer {
|
||||
|
||||
val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
|
||||
|
||||
override fun isOneShot(): Boolean = true
|
||||
|
||||
override fun contentType(): MediaType? = contentType
|
||||
|
||||
override fun contentLength(): Long = file.length()
|
||||
|
||||
override fun writeTo(sink: BufferedSink) {
|
||||
val source: Source
|
||||
var it: Iterator<OnDatatransferProgressListener>
|
||||
try {
|
||||
source = file.source()
|
||||
var transferred: Long = 0
|
||||
var read: Long
|
||||
while (source.read(sink.buffer, BYTES_TO_READ).also { read = it } != -1L) {
|
||||
transferred += read
|
||||
sink.flush()
|
||||
synchronized(dataTransferListeners) {
|
||||
it = dataTransferListeners.iterator()
|
||||
while (it.hasNext()) {
|
||||
it.next().onTransferProgress(read, transferred, file.length(), file.absolutePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
Timber.d("File with name ${file.name} and size ${file.length()} written in request body")
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||
synchronized(dataTransferListeners) {
|
||||
dataTransferListeners.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addDatatransferProgressListeners(listeners: Collection<OnDatatransferProgressListener>) {
|
||||
synchronized(dataTransferListeners) {
|
||||
dataTransferListeners.addAll(listeners)
|
||||
}
|
||||
}
|
||||
|
||||
override fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||
synchronized(dataTransferListeners) {
|
||||
dataTransferListeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val BYTES_TO_READ = 4_096L
|
||||
}
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.network;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
public class NetworkUtils {
|
||||
|
||||
private static String LOCAL_TRUSTSTORE_FILENAME = "knownServers.bks";
|
||||
|
||||
private static String LOCAL_TRUSTSTORE_PASSWORD = "password";
|
||||
|
||||
private static KeyStore mKnownServersStore = null;
|
||||
|
||||
/**
|
||||
* Returns the local store of reliable server certificates, explicitly accepted by the user.
|
||||
* <p>
|
||||
* Returns a KeyStore instance with empty content if the local store was never created.
|
||||
* <p>
|
||||
* Loads the store from the storage environment if needed.
|
||||
*
|
||||
* @param context Android context where the operation is being performed.
|
||||
* @return KeyStore instance with explicitly-accepted server certificates.
|
||||
* @throws KeyStoreException When the KeyStore instance could not be created.
|
||||
* @throws IOException When an existing local trust store could not be loaded.
|
||||
* @throws NoSuchAlgorithmException When the existing local trust store was saved with an unsupported algorithm.
|
||||
* @throws CertificateException When an exception occurred while loading the certificates from the local
|
||||
* trust store.
|
||||
*/
|
||||
public static KeyStore getKnownServersStore(Context context)
|
||||
throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
|
||||
if (mKnownServersStore == null) {
|
||||
//mKnownServersStore = KeyStore.getInstance("BKS");
|
||||
mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME);
|
||||
Timber.d("Searching known-servers store at %s", localTrustStoreFile.getAbsolutePath());
|
||||
if (localTrustStoreFile.exists()) {
|
||||
InputStream in = new FileInputStream(localTrustStoreFile);
|
||||
try {
|
||||
mKnownServersStore.load(in, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
} else {
|
||||
// next is necessary to initialize an empty KeyStore instance
|
||||
mKnownServersStore.load(null, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
|
||||
}
|
||||
}
|
||||
return mKnownServersStore;
|
||||
}
|
||||
|
||||
public static void addCertToKnownServersStore(Certificate cert, Context context)
|
||||
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
|
||||
|
||||
KeyStore knownServers = getKnownServersStore(context);
|
||||
knownServers.setCertificateEntry(Integer.toString(cert.hashCode()), cert);
|
||||
try (FileOutputStream fos = context.openFileOutput(LOCAL_TRUSTSTORE_FILENAME, Context.MODE_PRIVATE)) {
|
||||
knownServers.store(fos, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -26,5 +26,5 @@
|
||||
package com.owncloud.android.lib.common.network;
|
||||
|
||||
public interface OnDatatransferProgressListener {
|
||||
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileAbsoluteName);
|
||||
void onTransferProgress(long read, long transferred, long percent, String absolutePath);
|
||||
}
|
@ -26,14 +26,12 @@ package com.owncloud.android.lib.common.network;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
|
||||
public interface ProgressiveDataTransferer {
|
||||
|
||||
public void addDatatransferProgressListener (OnDatatransferProgressListener listener);
|
||||
void addDatatransferProgressListener(OnDatatransferProgressListener listener);
|
||||
|
||||
public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners);
|
||||
void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners);
|
||||
|
||||
public void removeDatatransferProgressListener(OnDatatransferProgressListener listener);
|
||||
void removeDatatransferProgressListener(OnDatatransferProgressListener listener);
|
||||
|
||||
}
|
@ -27,20 +27,19 @@
|
||||
|
||||
package com.owncloud.android.lib.common.network;
|
||||
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import com.owncloud.android.lib.common.http.HttpConstants;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* Aggregate saving the list of URLs followed in a sequence of redirections during the exceution of a
|
||||
* {@link com.owncloud.android.lib.common.operations.RemoteOperation}, and the status codes corresponding to all
|
||||
* {@link RemoteOperation}, and the status codes corresponding to all
|
||||
* of them.
|
||||
*
|
||||
* <p>
|
||||
* The last status code saved corresponds to the first response not being a redirection, unless the sequence exceeds
|
||||
* the maximum length of redirections allowed by the {@link com.owncloud.android.lib.common.OwnCloudClient} instance
|
||||
* that ran the operation.
|
||||
*
|
||||
* <p>
|
||||
* If no redirection was followed, the last (and first) status code contained corresponds to the original URL in the
|
||||
* request.
|
||||
*/
|
||||
@ -84,7 +83,6 @@ public class RedirectionPath {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new status code to the list of status corresponding to followed redirections.
|
||||
*
|
||||
@ -108,7 +106,7 @@ public class RedirectionPath {
|
||||
*/
|
||||
public String getLastPermanentLocation() {
|
||||
for (int i = mLastStatus; i >= 0; i--) {
|
||||
if (mStatuses[i] == HttpStatus.SC_MOVED_PERMANENTLY && i <= mLastLocation) {
|
||||
if (mStatuses[i] == HttpConstants.HTTP_MOVED_PERMANENTLY && i <= mLastLocation) {
|
||||
return mLocations[i];
|
||||
}
|
||||
}
|
||||
@ -122,5 +120,4 @@ public class RedirectionPath {
|
||||
return mLastLocation + 1;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* Copyright (C) 2012 Bartek Przybylski
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.network;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class WebdavUtils {
|
||||
|
||||
private static final SimpleDateFormat[] DATETIME_FORMATS = {
|
||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US),
|
||||
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
|
||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'", Locale.US),
|
||||
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US),
|
||||
new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US),
|
||||
new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
|
||||
new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US),
|
||||
new SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.US)
|
||||
};
|
||||
|
||||
public static Date parseResponseDate(String date) {
|
||||
Date returnDate;
|
||||
SimpleDateFormat format;
|
||||
for (SimpleDateFormat datetimeFormat : DATETIME_FORMATS) {
|
||||
try {
|
||||
format = datetimeFormat;
|
||||
synchronized (format) {
|
||||
returnDate = format.parse(date);
|
||||
}
|
||||
return returnDate;
|
||||
} catch (ParseException e) {
|
||||
// this is not the format
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a path according to URI RFC 2396.
|
||||
* <p>
|
||||
* If the received path doesn't start with "/", the method adds it.
|
||||
*
|
||||
* @param remoteFilePath Path
|
||||
* @return Encoded path according to RFC 2396, always starting with "/"
|
||||
*/
|
||||
public static String encodePath(String remoteFilePath) {
|
||||
String encodedPath = Uri.encode(remoteFilePath, "/");
|
||||
if (!encodedPath.startsWith("/")) {
|
||||
encodedPath = "/" + encodedPath;
|
||||
}
|
||||
return encodedPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param httpBaseMethod from which to get the etag
|
||||
* @return etag from response
|
||||
*/
|
||||
public static String getEtagFromResponse(HttpBaseMethod httpBaseMethod) {
|
||||
String eTag = httpBaseMethod.getResponseHeader("OC-ETag");
|
||||
if (eTag == null) {
|
||||
eTag = httpBaseMethod.getResponseHeader("oc-etag");
|
||||
}
|
||||
if (eTag == null) {
|
||||
eTag = httpBaseMethod.getResponseHeader("ETag");
|
||||
}
|
||||
if (eTag == null) {
|
||||
eTag = httpBaseMethod.getResponseHeader("etag");
|
||||
}
|
||||
String result = "";
|
||||
if (eTag != null) {
|
||||
result = eTag;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String normalizeProtocolPrefix(String url, boolean isSslConn) {
|
||||
if (!url.toLowerCase().startsWith("http://") &&
|
||||
!url.toLowerCase().startsWith("https://")) {
|
||||
if (isSslConn) {
|
||||
return "https://" + url;
|
||||
} else {
|
||||
return "http://" + url;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2017 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.operations;
|
||||
|
||||
import android.util.Xml;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Parser for server exceptions
|
||||
*
|
||||
* @author davidgonzalez
|
||||
*/
|
||||
public class ErrorMessageParser {
|
||||
// No namespaces
|
||||
private static final String ns = null;
|
||||
|
||||
// Nodes for XML Parser
|
||||
private static final String NODE_ERROR = "d:error";
|
||||
private static final String NODE_MESSAGE = "s:message";
|
||||
|
||||
/**
|
||||
* Parse exception response
|
||||
*
|
||||
* @param is
|
||||
* @return errorMessage for an exception
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
public String parseXMLResponse(InputStream is) throws XmlPullParserException,
|
||||
IOException {
|
||||
String errorMessage = "";
|
||||
|
||||
try {
|
||||
// XMLPullParser
|
||||
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
||||
parser.setInput(is, null);
|
||||
parser.nextTag();
|
||||
errorMessage = readError(parser);
|
||||
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse OCS node
|
||||
*
|
||||
* @param parser
|
||||
* @return reason for exception
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private String readError(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
String errorMessage = "";
|
||||
parser.require(XmlPullParser.START_TAG, ns, NODE_ERROR);
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
String name = parser.getName();
|
||||
// read NODE_MESSAGE
|
||||
if (name.equalsIgnoreCase(NODE_MESSAGE)) {
|
||||
errorMessage = readText(parser);
|
||||
} else {
|
||||
skip(parser);
|
||||
}
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip tags in parser procedure
|
||||
*
|
||||
* @param parser
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int depth = 1;
|
||||
while (depth != 0) {
|
||||
switch (parser.next()) {
|
||||
case XmlPullParser.END_TAG:
|
||||
depth--;
|
||||
break;
|
||||
case XmlPullParser.START_TAG:
|
||||
depth++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the text from a node
|
||||
*
|
||||
* @param parser
|
||||
* @return Text of the node
|
||||
* @throws IOException
|
||||
* @throws XmlPullParserException
|
||||
*/
|
||||
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||
String result = "";
|
||||
if (parser.next() == XmlPullParser.TEXT) {
|
||||
result = parser.getText();
|
||||
parser.nextTag();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.common.operations;
|
||||
|
||||
import android.util.Xml;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Parser for Invalid Character server exception
|
||||
*
|
||||
* @author masensio
|
||||
*/
|
||||
public class InvalidCharacterExceptionParser {
|
||||
|
||||
private static final String EXCEPTION_STRING = "OC\\Connector\\Sabre\\Exception\\InvalidPath";
|
||||
private static final String EXCEPTION_UPLOAD_STRING = "OCP\\Files\\InvalidPathException";
|
||||
|
||||
// No namespaces
|
||||
private static final String ns = null;
|
||||
|
||||
// Nodes for XML Parser
|
||||
private static final String NODE_ERROR = "d:error";
|
||||
private static final String NODE_EXCEPTION = "s:exception";
|
||||
|
||||
/**
|
||||
* Parse is as an Invalid Path Exception
|
||||
*
|
||||
* @param is
|
||||
* @return if The exception is an Invalid Char Exception
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean parseXMLResponse(InputStream is) throws XmlPullParserException,
|
||||
IOException {
|
||||
boolean result = false;
|
||||
|
||||
try {
|
||||
// XMLPullParser
|
||||
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
||||
parser.setInput(is, null);
|
||||
parser.nextTag();
|
||||
result = readError(parser);
|
||||
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse OCS node
|
||||
*
|
||||
* @param parser
|
||||
* @return List of ShareRemoteFiles
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private boolean readError(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
String exception = "";
|
||||
parser.require(XmlPullParser.START_TAG, ns, NODE_ERROR);
|
||||
while (parser.next() != XmlPullParser.END_TAG) {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
continue;
|
||||
}
|
||||
String name = parser.getName();
|
||||
// read NODE_EXCEPTION
|
||||
if (name.equalsIgnoreCase(NODE_EXCEPTION)) {
|
||||
exception = readText(parser);
|
||||
} else {
|
||||
skip(parser);
|
||||
}
|
||||
|
||||
}
|
||||
return exception.equalsIgnoreCase(EXCEPTION_STRING) ||
|
||||
exception.equalsIgnoreCase(EXCEPTION_UPLOAD_STRING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip tags in parser procedure
|
||||
*
|
||||
* @param parser
|
||||
* @throws XmlPullParserException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
if (parser.getEventType() != XmlPullParser.START_TAG) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int depth = 1;
|
||||
while (depth != 0) {
|
||||
switch (parser.next()) {
|
||||
case XmlPullParser.END_TAG:
|
||||
depth--;
|
||||
break;
|
||||
case XmlPullParser.START_TAG:
|
||||
depth++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the text from a node
|
||||
*
|
||||
* @param parser
|
||||
* @return Text of the node
|
||||
* @throws IOException
|
||||
* @throws XmlPullParserException
|
||||
*/
|
||||
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
|
||||
String result = "";
|
||||
if (parser.next() == XmlPullParser.TEXT) {
|
||||
result = parser.getText();
|
||||
parser.nextTag();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -24,7 +24,6 @@
|
||||
|
||||
package com.owncloud.android.lib.common.operations;
|
||||
|
||||
|
||||
public interface OnRemoteOperationListener {
|
||||
|
||||
void onRemoteOperationFinish(RemoteOperation caller, RemoteOperationResult result);
|
@ -0,0 +1,292 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.operations;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountsException;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudAccount;
|
||||
import com.owncloud.android.lib.common.OwnCloudClient;
|
||||
import com.owncloud.android.lib.common.SingleSessionManager;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import okhttp3.OkHttpClient;
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public abstract class RemoteOperation<T> implements Runnable {
|
||||
|
||||
/**
|
||||
* OCS API header name
|
||||
*/
|
||||
public static final String OCS_API_HEADER = "OCS-APIREQUEST";
|
||||
/**
|
||||
* OCS API header value
|
||||
*/
|
||||
public static final String OCS_API_HEADER_VALUE = "true";
|
||||
/**
|
||||
* ownCloud account in the remote ownCloud server to operate
|
||||
*/
|
||||
protected Account mAccount = null;
|
||||
|
||||
/**
|
||||
* Android Application context
|
||||
*/
|
||||
protected Context mContext = null;
|
||||
|
||||
/**
|
||||
* Object to interact with the remote server
|
||||
*/
|
||||
private OwnCloudClient mClient = null;
|
||||
|
||||
/**
|
||||
* Object to interact with the remote server
|
||||
*/
|
||||
private OkHttpClient mHttpClient = null;
|
||||
|
||||
/**
|
||||
* Callback object to notify about the execution of the remote operation
|
||||
*/
|
||||
private OnRemoteOperationListener mListener = null;
|
||||
|
||||
/**
|
||||
* Handler to the thread where mListener methods will be called
|
||||
*/
|
||||
private Handler mListenerHandler = null;
|
||||
|
||||
/**
|
||||
* Asynchronously executes the remote operation
|
||||
* <p>
|
||||
* This method should be used whenever an ownCloud account is available,
|
||||
* instead of {@link #execute(OwnCloudClient, OnRemoteOperationListener, Handler))}.
|
||||
*
|
||||
* @param account ownCloud account in remote ownCloud server to reach during the
|
||||
* execution of the operation.
|
||||
* @param context Android context for the component calling the method.
|
||||
* @param listener Listener to be notified about the execution of the operation.
|
||||
* @param listenerHandler Handler associated to the thread where the methods of the listener
|
||||
* objects must be called.
|
||||
* @return Thread were the remote operation is executed.
|
||||
*/
|
||||
public Thread execute(Account account, Context context,
|
||||
OnRemoteOperationListener listener, Handler listenerHandler) {
|
||||
|
||||
if (account == null) {
|
||||
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
|
||||
}
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
|
||||
}
|
||||
// mAccount and mContext in the runnerThread to create below
|
||||
mAccount = account;
|
||||
mContext = context.getApplicationContext();
|
||||
mClient = null; // the client instance will be created from
|
||||
|
||||
mListener = listener;
|
||||
|
||||
mListenerHandler = listenerHandler;
|
||||
|
||||
Thread runnerThread = new Thread(this);
|
||||
runnerThread.start();
|
||||
return runnerThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously executes the remote operation
|
||||
*
|
||||
* @param client Client object to reach an ownCloud server
|
||||
* during the execution of the operation.
|
||||
* @param listener Listener to be notified about the execution of the operation.
|
||||
* @param listenerHandler Handler, if passed in, associated to the thread where the methods of
|
||||
* the listener objects must be called.
|
||||
* @return Thread were the remote operation is executed.
|
||||
*/
|
||||
public Thread execute(OwnCloudClient client, OnRemoteOperationListener listener, Handler listenerHandler) {
|
||||
if (client == null) {
|
||||
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL OwnCloudClient");
|
||||
}
|
||||
mClient = client;
|
||||
if (client.getAccount() != null) {
|
||||
mAccount = client.getAccount().getSavedAccount();
|
||||
}
|
||||
mContext = client.getContext();
|
||||
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException
|
||||
("Trying to execute a remote operation asynchronously without a listener to notify the result");
|
||||
}
|
||||
mListener = listener;
|
||||
|
||||
if (listenerHandler != null) {
|
||||
mListenerHandler = listenerHandler;
|
||||
}
|
||||
|
||||
Thread runnerThread = new Thread(this);
|
||||
runnerThread.start();
|
||||
return runnerThread;
|
||||
}
|
||||
|
||||
private void grantOwnCloudClient() throws
|
||||
AccountUtils.AccountNotFoundException, OperationCanceledException, AuthenticatorException, IOException {
|
||||
if (mClient == null) {
|
||||
if (mAccount != null && mContext != null) {
|
||||
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
|
||||
mClient = SingleSessionManager.getDefaultSingleton().
|
||||
getClientFor(ocAccount, mContext, SingleSessionManager.getConnectionValidator());
|
||||
} else {
|
||||
throw new IllegalStateException("Trying to run a remote operation " +
|
||||
"asynchronously with no client and no chance to create one (no account)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current client instance to access the remote server.
|
||||
*
|
||||
* @return Current client instance to access the remote server.
|
||||
*/
|
||||
public final OwnCloudClient getClient() {
|
||||
return mClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method to implement the operation in derived classes.
|
||||
*/
|
||||
protected abstract RemoteOperationResult<T> run(OwnCloudClient client);
|
||||
|
||||
/**
|
||||
* Synchronously executes the remote operation on the received ownCloud account.
|
||||
* <p>
|
||||
* Do not call this method from the main thread.
|
||||
* <p>
|
||||
* This method should be used whenever an ownCloud account is available, instead of
|
||||
* {@link #execute(OwnCloudClient)}.
|
||||
*
|
||||
* @param account ownCloud account in remote ownCloud server to reach during the
|
||||
* execution of the operation.
|
||||
* @param context Android context for the component calling the method.
|
||||
* @return Result of the operation.
|
||||
*/
|
||||
public RemoteOperationResult<T> execute(Account account, Context context) {
|
||||
if (account == null) {
|
||||
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
|
||||
}
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
|
||||
}
|
||||
mAccount = account;
|
||||
mContext = context.getApplicationContext();
|
||||
|
||||
return runOperation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously executes the remote operation
|
||||
* <p>
|
||||
* Do not call this method from the main thread.
|
||||
*
|
||||
* @param client Client object to reach an ownCloud server during the execution of
|
||||
* the operation.
|
||||
* @return Result of the operation.
|
||||
*/
|
||||
public RemoteOperationResult<T> execute(OwnCloudClient client) {
|
||||
if (client == null) {
|
||||
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL OwnCloudClient");
|
||||
}
|
||||
mClient = client;
|
||||
if (client.getAccount() != null) {
|
||||
mAccount = client.getAccount().getSavedAccount();
|
||||
}
|
||||
mContext = client.getContext();
|
||||
|
||||
return runOperation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously executes the remote operation
|
||||
* <p>
|
||||
* Do not call this method from the main thread.
|
||||
*
|
||||
* @param client Client object to reach an ownCloud server during the execution of
|
||||
* the operation.
|
||||
* @return Result of the operation.
|
||||
*/
|
||||
public RemoteOperationResult<T> execute(OkHttpClient client, Context context) {
|
||||
if (client == null) {
|
||||
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL OwnCloudClient");
|
||||
}
|
||||
mHttpClient = client;
|
||||
mContext = context;
|
||||
|
||||
return runOperation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run operation for asynchronous or synchronous 'onExecute' method.
|
||||
* <p>
|
||||
* Considers and performs silent refresh of account credentials if possible
|
||||
*
|
||||
* @return Remote operation result
|
||||
*/
|
||||
private RemoteOperationResult<T> runOperation() {
|
||||
|
||||
RemoteOperationResult<T> result;
|
||||
|
||||
try {
|
||||
grantOwnCloudClient();
|
||||
result = run(mClient);
|
||||
|
||||
} catch (AccountsException | IOException e) {
|
||||
Timber.e(e, "Error while trying to access to %s", mAccount.name);
|
||||
result = new RemoteOperationResult<>(e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronous execution of the operation
|
||||
* started by {@link RemoteOperation#execute(OwnCloudClient,
|
||||
* OnRemoteOperationListener, Handler)},
|
||||
* and result posting.
|
||||
*/
|
||||
@Override
|
||||
public final void run() {
|
||||
|
||||
final RemoteOperationResult resultToSend = runOperation();
|
||||
|
||||
if (mListenerHandler != null && mListener != null) {
|
||||
mListenerHandler.post(() ->
|
||||
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend));
|
||||
} else if (mListener != null) {
|
||||
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -24,114 +24,55 @@
|
||||
|
||||
package com.owncloud.android.lib.common.operations;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountsException;
|
||||
|
||||
import at.bitfire.dav4jvm.exception.DavException;
|
||||
import at.bitfire.dav4jvm.exception.HttpException;
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils;
|
||||
import com.owncloud.android.lib.common.http.HttpConstants;
|
||||
import com.owncloud.android.lib.common.http.methods.HttpBaseMethod;
|
||||
import com.owncloud.android.lib.common.network.CertificateCombinedException;
|
||||
import okhttp3.Headers;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.json.JSONException;
|
||||
import timber.log.Timber;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountsException;
|
||||
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
|
||||
import com.owncloud.android.lib.common.network.CertificateCombinedException;
|
||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||
|
||||
import org.apache.commons.httpclient.ConnectTimeoutException;
|
||||
import org.apache.commons.httpclient.Header;
|
||||
import org.apache.commons.httpclient.HttpException;
|
||||
import org.apache.commons.httpclient.HttpMethod;
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.json.JSONException;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
|
||||
/**
|
||||
* The result of a remote operation required to an ownCloud server.
|
||||
* <p/>
|
||||
* Provides a common classification of remote operation results for all the
|
||||
* application.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
*/
|
||||
public class RemoteOperationResult implements Serializable {
|
||||
public class RemoteOperationResult<T>
|
||||
implements Serializable {
|
||||
|
||||
/**
|
||||
* Generated - should be refreshed every time the class changes!!
|
||||
*/
|
||||
private static final long serialVersionUID = 4968939884332372230L;
|
||||
|
||||
private static final String TAG = RemoteOperationResult.class.getSimpleName();
|
||||
|
||||
public enum ResultCode {
|
||||
OK,
|
||||
OK_SSL,
|
||||
OK_NO_SSL,
|
||||
UNHANDLED_HTTP_CODE,
|
||||
UNAUTHORIZED,
|
||||
FILE_NOT_FOUND,
|
||||
INSTANCE_NOT_CONFIGURED,
|
||||
UNKNOWN_ERROR,
|
||||
WRONG_CONNECTION,
|
||||
TIMEOUT,
|
||||
INCORRECT_ADDRESS,
|
||||
HOST_NOT_AVAILABLE,
|
||||
NO_NETWORK_CONNECTION,
|
||||
SSL_ERROR,
|
||||
SSL_RECOVERABLE_PEER_UNVERIFIED,
|
||||
BAD_OC_VERSION,
|
||||
CANCELLED,
|
||||
INVALID_LOCAL_FILE_NAME,
|
||||
INVALID_OVERWRITE,
|
||||
CONFLICT,
|
||||
OAUTH2_ERROR,
|
||||
SYNC_CONFLICT,
|
||||
LOCAL_STORAGE_FULL,
|
||||
LOCAL_STORAGE_NOT_MOVED,
|
||||
LOCAL_STORAGE_NOT_COPIED,
|
||||
OAUTH2_ERROR_ACCESS_DENIED,
|
||||
QUOTA_EXCEEDED,
|
||||
ACCOUNT_NOT_FOUND,
|
||||
ACCOUNT_EXCEPTION,
|
||||
ACCOUNT_NOT_NEW,
|
||||
ACCOUNT_NOT_THE_SAME,
|
||||
INVALID_CHARACTER_IN_NAME,
|
||||
SHARE_NOT_FOUND,
|
||||
LOCAL_STORAGE_NOT_REMOVED,
|
||||
FORBIDDEN,
|
||||
SHARE_FORBIDDEN,
|
||||
SPECIFIC_FORBIDDEN,
|
||||
OK_REDIRECT_TO_NON_SECURE_CONNECTION,
|
||||
INVALID_MOVE_INTO_DESCENDANT,
|
||||
INVALID_COPY_INTO_DESCENDANT,
|
||||
PARTIAL_MOVE_DONE,
|
||||
PARTIAL_COPY_DONE,
|
||||
SHARE_WRONG_PARAMETER,
|
||||
WRONG_SERVER_RESPONSE,
|
||||
INVALID_CHARACTER_DETECT_IN_SERVER,
|
||||
DELAYED_FOR_WIFI,
|
||||
LOCAL_FILE_NOT_FOUND,
|
||||
SERVICE_UNAVAILABLE,
|
||||
SPECIFIC_SERVICE_UNAVAILABLE
|
||||
}
|
||||
private static final String LOCATION = "location";
|
||||
private static final String WWW_AUTHENTICATE = "www-authenticate";
|
||||
|
||||
private boolean mSuccess = false;
|
||||
private int mHttpCode = -1;
|
||||
private String mHttpPhrase = null;
|
||||
private Exception mException = null;
|
||||
private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
|
||||
private String mRedirectedLocation;
|
||||
private String mAuthenticate;
|
||||
private String mRedirectedLocation = "";
|
||||
private List<String> mAuthenticate = new ArrayList<>();
|
||||
private String mLastPermanentLocation = null;
|
||||
|
||||
private ArrayList<Object> mData;
|
||||
private T mData = null;
|
||||
|
||||
/**
|
||||
* Public constructor from result code.
|
||||
@ -145,7 +86,23 @@ public class RemoteOperationResult implements Serializable {
|
||||
mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL ||
|
||||
code == ResultCode.OK_NO_SSL ||
|
||||
code == ResultCode.OK_REDIRECT_TO_NON_SECURE_CONNECTION);
|
||||
mData = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RemoteOperationResult based on the result given by a previous one.
|
||||
* It does not copy the data.
|
||||
*
|
||||
* @param prevRemoteOperation
|
||||
*/
|
||||
public RemoteOperationResult(RemoteOperationResult prevRemoteOperation) {
|
||||
mCode = prevRemoteOperation.mCode;
|
||||
mHttpCode = prevRemoteOperation.mHttpCode;
|
||||
mHttpPhrase = prevRemoteOperation.mHttpPhrase;
|
||||
mAuthenticate = prevRemoteOperation.mAuthenticate;
|
||||
mException = prevRemoteOperation.mException;
|
||||
mLastPermanentLocation = prevRemoteOperation.mLastPermanentLocation;
|
||||
mSuccess = prevRemoteOperation.mSuccess;
|
||||
mRedirectedLocation = prevRemoteOperation.mRedirectedLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -159,6 +116,14 @@ public class RemoteOperationResult implements Serializable {
|
||||
*/
|
||||
public RemoteOperationResult(Exception e) {
|
||||
mException = e;
|
||||
//TODO: Do propper exception handling and remove this
|
||||
Timber.e("---------------------------------" +
|
||||
"\nCreate RemoteOperationResult from exception." +
|
||||
"\n Message: %s" +
|
||||
"\n Stacktrace: %s" +
|
||||
"\n---------------------------------",
|
||||
ExceptionUtils.getMessage(e),
|
||||
ExceptionUtils.getStackTrace(e));
|
||||
|
||||
if (e instanceof OperationCancelledException) {
|
||||
mCode = ResultCode.CANCELLED;
|
||||
@ -169,22 +134,22 @@ public class RemoteOperationResult implements Serializable {
|
||||
} else if (e instanceof SocketTimeoutException) {
|
||||
mCode = ResultCode.TIMEOUT;
|
||||
|
||||
} else if (e instanceof ConnectTimeoutException) {
|
||||
mCode = ResultCode.TIMEOUT;
|
||||
|
||||
} else if (e instanceof MalformedURLException) {
|
||||
mCode = ResultCode.INCORRECT_ADDRESS;
|
||||
|
||||
} else if (e instanceof UnknownHostException) {
|
||||
mCode = ResultCode.HOST_NOT_AVAILABLE;
|
||||
|
||||
} else if (e instanceof AccountNotFoundException) {
|
||||
} else if (e instanceof AccountUtils.AccountNotFoundException) {
|
||||
mCode = ResultCode.ACCOUNT_NOT_FOUND;
|
||||
|
||||
} else if (e instanceof AccountsException) {
|
||||
mCode = ResultCode.ACCOUNT_EXCEPTION;
|
||||
|
||||
} else if (e instanceof SSLException || e instanceof RuntimeException) {
|
||||
if (e instanceof SSLPeerUnverifiedException) {
|
||||
mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
|
||||
} else {
|
||||
CertificateCombinedException se = getCertificateCombinedException(e);
|
||||
if (se != null) {
|
||||
mException = se;
|
||||
@ -197,14 +162,17 @@ public class RemoteOperationResult implements Serializable {
|
||||
} else {
|
||||
mCode = ResultCode.SSL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (e instanceof FileNotFoundException) {
|
||||
mCode = ResultCode.LOCAL_FILE_NOT_FOUND;
|
||||
|
||||
} else {
|
||||
} else if (e instanceof ProtocolException) {
|
||||
mCode = ResultCode.NETWORK_ERROR;
|
||||
}
|
||||
else {
|
||||
mCode = ResultCode.UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,75 +182,71 @@ public class RemoteOperationResult implements Serializable {
|
||||
* <p>
|
||||
* Determines a {@link ResultCode} from the already executed method received as a parameter. Generally,
|
||||
* will depend on the HTTP code and HTTP response headers received. In some cases will inspect also the
|
||||
* response body.
|
||||
* response body
|
||||
*
|
||||
* @param success The operation was considered successful or not.
|
||||
* @param httpMethod HTTP/DAV method already executed which response will be examined to interpret the
|
||||
* result.
|
||||
* @param httpMethod
|
||||
* @throws IOException
|
||||
*/
|
||||
public RemoteOperationResult(boolean success, HttpMethod httpMethod) throws IOException {
|
||||
this(
|
||||
success,
|
||||
httpMethod.getStatusCode(),
|
||||
httpMethod.getStatusText(),
|
||||
public RemoteOperationResult(HttpBaseMethod httpMethod) throws IOException {
|
||||
this(httpMethod.getStatusCode(),
|
||||
httpMethod.getStatusMessage(),
|
||||
httpMethod.getResponseHeaders()
|
||||
);
|
||||
|
||||
if (mHttpCode == HttpStatus.SC_BAD_REQUEST) { // 400
|
||||
if (mHttpCode == HttpConstants.HTTP_BAD_REQUEST) { // 400
|
||||
String bodyResponse = httpMethod.getResponseBodyAsString();
|
||||
// do not get for other HTTP codes!; could not be available
|
||||
|
||||
// do not get for other HTTP codes!; could not be available
|
||||
if (bodyResponse != null && bodyResponse.length() > 0) {
|
||||
InputStream is = new ByteArrayInputStream(bodyResponse.getBytes());
|
||||
InvalidCharacterExceptionParser xmlParser = new InvalidCharacterExceptionParser();
|
||||
try {
|
||||
if (xmlParser.parseXMLResponse(is)) {
|
||||
mCode = ResultCode.INVALID_CHARACTER_DETECT_IN_SERVER;
|
||||
} else {
|
||||
parseErrorMessageAndSetCode(
|
||||
httpMethod.getResponseBodyAsString(),
|
||||
ResultCode.SPECIFIC_BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log_OC.w(TAG, "Error reading exception from server: " + e.getMessage());
|
||||
Timber.w("Error reading exception from server: %s", e.getMessage());
|
||||
// mCode stays as set in this(success, httpCode, headers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mHttpCode == HttpStatus.SC_FORBIDDEN) {
|
||||
String bodyResponse = httpMethod.getResponseBodyAsString();
|
||||
|
||||
if (bodyResponse != null && bodyResponse.length() > 0) {
|
||||
InputStream is = new ByteArrayInputStream(bodyResponse.getBytes());
|
||||
ErrorMessageParser xmlParser = new ErrorMessageParser();
|
||||
try {
|
||||
String errorMessage = xmlParser.parseXMLResponse(is);
|
||||
if (errorMessage != null && errorMessage != "") {
|
||||
mCode = ResultCode.SPECIFIC_FORBIDDEN;
|
||||
mHttpPhrase = errorMessage;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log_OC.w(TAG, "Error reading exception from server: " + e.getMessage());
|
||||
// mCode stays as set in this(success, httpCode, headers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mHttpCode == HttpStatus.SC_SERVICE_UNAVAILABLE) {
|
||||
String bodyResponse = httpMethod.getResponseBodyAsString();
|
||||
|
||||
if (bodyResponse != null && bodyResponse.length() > 0) {
|
||||
InputStream is = new ByteArrayInputStream(bodyResponse.getBytes());
|
||||
ErrorMessageParser xmlParser = new ErrorMessageParser();
|
||||
try {
|
||||
String errorMessage = xmlParser.parseXMLResponse(is);
|
||||
if (errorMessage != "" && errorMessage != null) {
|
||||
mCode = ResultCode.SPECIFIC_SERVICE_UNAVAILABLE;
|
||||
mHttpPhrase = errorMessage;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log_OC.w(TAG, "Error reading exception from server: " + e.getMessage());
|
||||
// mCode stays as set in this(success, httpCode, headers)
|
||||
}
|
||||
}
|
||||
// before
|
||||
switch (mHttpCode) {
|
||||
case HttpConstants.HTTP_FORBIDDEN:
|
||||
parseErrorMessageAndSetCode(
|
||||
httpMethod.getResponseBodyAsString(),
|
||||
ResultCode.SPECIFIC_FORBIDDEN
|
||||
);
|
||||
break;
|
||||
case HttpConstants.HTTP_UNSUPPORTED_MEDIA_TYPE:
|
||||
parseErrorMessageAndSetCode(
|
||||
httpMethod.getResponseBodyAsString(),
|
||||
ResultCode.SPECIFIC_UNSUPPORTED_MEDIA_TYPE
|
||||
);
|
||||
break;
|
||||
case HttpConstants.HTTP_SERVICE_UNAVAILABLE:
|
||||
parseErrorMessageAndSetCode(
|
||||
httpMethod.getResponseBodyAsString(),
|
||||
ResultCode.SPECIFIC_SERVICE_UNAVAILABLE
|
||||
);
|
||||
break;
|
||||
case HttpConstants.HTTP_METHOD_NOT_ALLOWED:
|
||||
parseErrorMessageAndSetCode(
|
||||
httpMethod.getResponseBodyAsString(),
|
||||
ResultCode.SPECIFIC_METHOD_NOT_ALLOWED
|
||||
);
|
||||
break;
|
||||
case HttpConstants.HTTP_TOO_EARLY:
|
||||
mCode = ResultCode.TOO_EARLY;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,99 +256,104 @@ public class RemoteOperationResult implements Serializable {
|
||||
* To be used when the result needs to be interpreted from HTTP response elements that could come from
|
||||
* different requests (WARNING: black magic, try to avoid).
|
||||
* <p>
|
||||
* If all the fields come from the same HTTP/DAV response, {@link #RemoteOperationResult(boolean, HttpMethod)}
|
||||
* should be used instead.
|
||||
* <p>
|
||||
* Determines a {@link ResultCode} depending on the HTTP code and HTTP response headers received.
|
||||
*
|
||||
* @param success The operation was considered successful or not.
|
||||
* @param httpCode HTTP status code returned by an HTTP/DAV method.
|
||||
* @param httpPhrase HTTP status line phrase returned by an HTTP/DAV method
|
||||
* @param httpHeaders HTTP response header returned by an HTTP/DAV method
|
||||
* @param headers HTTP response header returned by an HTTP/DAV method
|
||||
*/
|
||||
public RemoteOperationResult(boolean success, int httpCode, String httpPhrase, Header[] httpHeaders) {
|
||||
this(success, httpCode, httpPhrase);
|
||||
if (httpHeaders != null) {
|
||||
Header current;
|
||||
for (Header httpHeader : httpHeaders) {
|
||||
current = httpHeader;
|
||||
if ("location".equals(current.getName().toLowerCase())) {
|
||||
mRedirectedLocation = current.getValue();
|
||||
public RemoteOperationResult(int httpCode, String httpPhrase, Headers headers) {
|
||||
this(httpCode, httpPhrase);
|
||||
if (headers != null) {
|
||||
for (Map.Entry<String, List<String>> header : headers.toMultimap().entrySet()) {
|
||||
if (LOCATION.equalsIgnoreCase(header.getKey())) {
|
||||
mRedirectedLocation = header.getValue().get(0);
|
||||
continue;
|
||||
}
|
||||
if ("www-authenticate".equals(current.getName().toLowerCase())) {
|
||||
mAuthenticate = current.getValue();
|
||||
break;
|
||||
if (WWW_AUTHENTICATE.equalsIgnoreCase(header.getKey())) {
|
||||
for (String value: header.getValue()) {
|
||||
mAuthenticate.add(value.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isIdPRedirection()) {
|
||||
mCode = ResultCode.UNAUTHORIZED; // overrides default ResultCode.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor for results built interpreting a HTTP or DAV response.
|
||||
*
|
||||
* <p>
|
||||
* Determines a {@link ResultCode} depending of the type of the exception.
|
||||
*
|
||||
* @param success Operation was successful or not.
|
||||
* @param httpCode HTTP status code returned by the HTTP/DAV method.
|
||||
* @param httpPhrase HTTP status line phrase returned by the HTTP/DAV method
|
||||
*/
|
||||
private RemoteOperationResult(boolean success, int httpCode, String httpPhrase) {
|
||||
mSuccess = success;
|
||||
private RemoteOperationResult(int httpCode, String httpPhrase) {
|
||||
mHttpCode = httpCode;
|
||||
mHttpPhrase = httpPhrase;
|
||||
|
||||
if (success) {
|
||||
mCode = ResultCode.OK;
|
||||
|
||||
} else if (httpCode > 0) {
|
||||
if (httpCode > 0) {
|
||||
switch (httpCode) {
|
||||
case HttpStatus.SC_UNAUTHORIZED: // 401
|
||||
case HttpConstants.HTTP_UNAUTHORIZED: // 401
|
||||
mCode = ResultCode.UNAUTHORIZED;
|
||||
break;
|
||||
case HttpStatus.SC_FORBIDDEN: // 403
|
||||
case HttpConstants.HTTP_FORBIDDEN: // 403
|
||||
mCode = ResultCode.FORBIDDEN;
|
||||
break;
|
||||
case HttpStatus.SC_NOT_FOUND: // 404
|
||||
case HttpConstants.HTTP_NOT_FOUND: // 404
|
||||
mCode = ResultCode.FILE_NOT_FOUND;
|
||||
break;
|
||||
case HttpStatus.SC_CONFLICT: // 409
|
||||
case HttpConstants.HTTP_CONFLICT: // 409
|
||||
mCode = ResultCode.CONFLICT;
|
||||
break;
|
||||
case HttpStatus.SC_INTERNAL_SERVER_ERROR: // 500
|
||||
case HttpConstants.HTTP_INTERNAL_SERVER_ERROR: // 500
|
||||
mCode = ResultCode.INSTANCE_NOT_CONFIGURED; // assuming too much...
|
||||
break;
|
||||
case HttpStatus.SC_SERVICE_UNAVAILABLE: // 503
|
||||
case HttpConstants.HTTP_SERVICE_UNAVAILABLE: // 503
|
||||
mCode = ResultCode.SERVICE_UNAVAILABLE;
|
||||
break;
|
||||
case HttpStatus.SC_INSUFFICIENT_STORAGE: // 507
|
||||
case HttpConstants.HTTP_INSUFFICIENT_STORAGE: // 507
|
||||
mCode = ResultCode.QUOTA_EXCEEDED; // surprise!
|
||||
break;
|
||||
default:
|
||||
mCode = ResultCode.UNHANDLED_HTTP_CODE; // UNKNOWN ERROR
|
||||
Log_OC.d(TAG,
|
||||
"RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " +
|
||||
mHttpCode + " " + mHttpPhrase
|
||||
);
|
||||
Timber.d("RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + mHttpCode + " " + mHttpPhrase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setData(ArrayList<Object> files) {
|
||||
mData = files;
|
||||
/**
|
||||
* Parse the error message included in the body response, if any, and set the specific result
|
||||
* code
|
||||
*
|
||||
* @param bodyResponse okHttp response body
|
||||
* @param resultCode our own custom result code
|
||||
*/
|
||||
private void parseErrorMessageAndSetCode(String bodyResponse, ResultCode resultCode) {
|
||||
if (bodyResponse != null && bodyResponse.length() > 0) {
|
||||
InputStream is = new ByteArrayInputStream(bodyResponse.getBytes());
|
||||
ErrorMessageParser xmlParser = new ErrorMessageParser();
|
||||
try {
|
||||
String errorMessage = xmlParser.parseXMLResponse(is);
|
||||
if (!errorMessage.equals("")) {
|
||||
mCode = resultCode;
|
||||
mHttpPhrase = errorMessage;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Timber.w("Error reading exception from server: %s\nTrace: %s", e.getMessage(), ExceptionUtils.getStackTrace(e));
|
||||
// mCode stays as set in this(success, httpCode, headers)
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<Object> getData() {
|
||||
return mData;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return mSuccess;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.mSuccess = success;
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return mCode == ResultCode.CANCELLED;
|
||||
}
|
||||
@ -425,7 +394,7 @@ public class RemoteOperationResult implements Serializable {
|
||||
previousCause = cause;
|
||||
cause = cause.getCause();
|
||||
}
|
||||
if (cause != null && cause instanceof CertificateCombinedException) {
|
||||
if (cause instanceof CertificateCombinedException) {
|
||||
result = (CertificateCombinedException) cause;
|
||||
}
|
||||
return result;
|
||||
@ -443,9 +412,6 @@ public class RemoteOperationResult implements Serializable {
|
||||
} else if (mException instanceof SocketTimeoutException) {
|
||||
return "Socket timeout exception";
|
||||
|
||||
} else if (mException instanceof ConnectTimeoutException) {
|
||||
return "Connect timeout exception";
|
||||
|
||||
} else if (mException instanceof MalformedURLException) {
|
||||
return "Malformed URL exception";
|
||||
|
||||
@ -453,10 +419,11 @@ public class RemoteOperationResult implements Serializable {
|
||||
return "Unknown host exception";
|
||||
|
||||
} else if (mException instanceof CertificateCombinedException) {
|
||||
if (((CertificateCombinedException) mException).isRecoverable())
|
||||
if (((CertificateCombinedException) mException).isRecoverable()) {
|
||||
return "SSL recoverable exception";
|
||||
else
|
||||
} else {
|
||||
return "SSL exception";
|
||||
}
|
||||
|
||||
} else if (mException instanceof SSLException) {
|
||||
return "SSL exception";
|
||||
@ -470,9 +437,9 @@ public class RemoteOperationResult implements Serializable {
|
||||
} else if (mException instanceof IOException) {
|
||||
return "Unrecovered transport exception";
|
||||
|
||||
} else if (mException instanceof AccountNotFoundException) {
|
||||
} else if (mException instanceof AccountUtils.AccountNotFoundException) {
|
||||
Account failedAccount =
|
||||
((AccountNotFoundException) mException).getFailedAccount();
|
||||
((AccountUtils.AccountNotFoundException) mException).getFailedAccount();
|
||||
return mException.getMessage() + " (" +
|
||||
(failedAccount != null ? failedAccount.name : "NULL") + ")";
|
||||
|
||||
@ -524,7 +491,7 @@ public class RemoteOperationResult implements Serializable {
|
||||
}
|
||||
|
||||
public boolean isServerFail() {
|
||||
return (mHttpCode >= HttpStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
return (mHttpCode >= HttpConstants.HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
public boolean isException() {
|
||||
@ -539,12 +506,6 @@ public class RemoteOperationResult implements Serializable {
|
||||
return mRedirectedLocation;
|
||||
}
|
||||
|
||||
public boolean isIdPRedirection() {
|
||||
return (mRedirectedLocation != null &&
|
||||
(mRedirectedLocation.toUpperCase().contains("SAML") ||
|
||||
mRedirectedLocation.toLowerCase().contains("wayf")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if is a non https connection
|
||||
*
|
||||
@ -554,7 +515,7 @@ public class RemoteOperationResult implements Serializable {
|
||||
return (mRedirectedLocation != null && !(mRedirectedLocation.toLowerCase().startsWith("https://")));
|
||||
}
|
||||
|
||||
public String getAuthenticateHeader() {
|
||||
public List<String> getAuthenticateHeaders() {
|
||||
return mAuthenticate;
|
||||
}
|
||||
|
||||
@ -566,4 +527,72 @@ public class RemoteOperationResult implements Serializable {
|
||||
mLastPermanentLocation = lastPermanentLocation;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return mData;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
mData = data;
|
||||
}
|
||||
|
||||
public void setHttpPhrase(String httpPhrase) {
|
||||
mHttpPhrase = httpPhrase;
|
||||
}
|
||||
|
||||
public enum ResultCode {
|
||||
OK,
|
||||
OK_SSL,
|
||||
OK_NO_SSL,
|
||||
UNHANDLED_HTTP_CODE,
|
||||
UNAUTHORIZED,
|
||||
FILE_NOT_FOUND,
|
||||
INSTANCE_NOT_CONFIGURED,
|
||||
UNKNOWN_ERROR,
|
||||
WRONG_CONNECTION,
|
||||
TIMEOUT,
|
||||
INCORRECT_ADDRESS,
|
||||
HOST_NOT_AVAILABLE,
|
||||
NO_NETWORK_CONNECTION,
|
||||
SSL_ERROR,
|
||||
SSL_RECOVERABLE_PEER_UNVERIFIED,
|
||||
BAD_OC_VERSION,
|
||||
CANCELLED,
|
||||
INVALID_LOCAL_FILE_NAME,
|
||||
INVALID_OVERWRITE,
|
||||
CONFLICT,
|
||||
OAUTH2_ERROR,
|
||||
SYNC_CONFLICT,
|
||||
LOCAL_STORAGE_FULL,
|
||||
LOCAL_STORAGE_NOT_MOVED,
|
||||
LOCAL_STORAGE_NOT_COPIED,
|
||||
OAUTH2_ERROR_ACCESS_DENIED,
|
||||
QUOTA_EXCEEDED,
|
||||
ACCOUNT_NOT_FOUND,
|
||||
ACCOUNT_EXCEPTION,
|
||||
ACCOUNT_NOT_NEW,
|
||||
ACCOUNT_NOT_THE_SAME,
|
||||
INVALID_CHARACTER_IN_NAME,
|
||||
SHARE_NOT_FOUND,
|
||||
LOCAL_STORAGE_NOT_REMOVED,
|
||||
FORBIDDEN,
|
||||
SHARE_FORBIDDEN,
|
||||
SPECIFIC_FORBIDDEN,
|
||||
OK_REDIRECT_TO_NON_SECURE_CONNECTION,
|
||||
INVALID_MOVE_INTO_DESCENDANT,
|
||||
INVALID_COPY_INTO_DESCENDANT,
|
||||
PARTIAL_MOVE_DONE,
|
||||
PARTIAL_COPY_DONE,
|
||||
SHARE_WRONG_PARAMETER,
|
||||
WRONG_SERVER_RESPONSE,
|
||||
INVALID_CHARACTER_DETECT_IN_SERVER,
|
||||
DELAYED_FOR_WIFI,
|
||||
LOCAL_FILE_NOT_FOUND,
|
||||
SERVICE_UNAVAILABLE,
|
||||
SPECIFIC_SERVICE_UNAVAILABLE,
|
||||
SPECIFIC_UNSUPPORTED_MEDIA_TYPE,
|
||||
SPECIFIC_METHOD_NOT_ALLOWED,
|
||||
SPECIFIC_BAD_REQUEST,
|
||||
TOO_EARLY,
|
||||
NETWORK_ERROR,
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.utils
|
||||
|
||||
fun Any.isOneOf(vararg values: Any): Boolean {
|
||||
return this in values
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -22,34 +22,27 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common;
|
||||
package com.owncloud.android.lib.common.utils
|
||||
|
||||
import java.io.IOException;
|
||||
import info.hannes.timber.FileLoggingTree
|
||||
import info.hannes.timber.fileLoggingTree
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.content.Context;
|
||||
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
|
||||
|
||||
|
||||
/**
|
||||
* Manager to create and reuse OwnCloudClient instances to access remote OC servers.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* @author masensio
|
||||
*/
|
||||
|
||||
public interface OwnCloudClientManager {
|
||||
|
||||
public OwnCloudClient getClientFor(OwnCloudAccount account, Context context)
|
||||
throws AccountNotFoundException, OperationCanceledException, AuthenticatorException,
|
||||
IOException;
|
||||
|
||||
public OwnCloudClient removeClientFor(OwnCloudAccount account);
|
||||
|
||||
public void saveAllClients(Context context, String accountType)
|
||||
throws AccountNotFoundException, AuthenticatorException,
|
||||
IOException, OperationCanceledException;
|
||||
object LoggingHelper {
|
||||
|
||||
fun startLogging(directory: File, storagePath: String) {
|
||||
fileLoggingTree()?.let {
|
||||
Timber.uproot(it)
|
||||
}
|
||||
if (!directory.exists())
|
||||
directory.mkdirs()
|
||||
Timber.plant(FileLoggingTree(directory, filename = storagePath))
|
||||
}
|
||||
|
||||
fun stopLogging() {
|
||||
fileLoggingTree()?.let {
|
||||
Timber.uproot(it)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.common.utils;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Class with methods to generate random values
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
public class RandomUtils {
|
||||
|
||||
private static final String CANDIDATECHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||
"1234567890-+/_=.:";
|
||||
|
||||
/**
|
||||
* @param length the number of random chars to be generated
|
||||
* @return String containing random chars
|
||||
*/
|
||||
public static String generateRandomString(int length) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Random random = new Random();
|
||||
for (int i = 0; i < length; i++) {
|
||||
sb.append(CANDIDATECHARS.charAt(random.nextInt(CANDIDATECHARS.length())));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param min minimum integer to obtain randomly
|
||||
* @param max maximum integer to obtain randomly
|
||||
* @return random integer between min and max
|
||||
*/
|
||||
public static int generateRandomInteger(int min, int max) {
|
||||
Random r = new Random();
|
||||
return r.nextInt(max - min) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return random UUID
|
||||
*/
|
||||
public static String generateRandomUUID() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
*
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.owncloud.android.lib.resources
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
// Response retrieved by OCS Rest API, used to obtain capabilities, shares and user info among others.
|
||||
// More info: https://doc.owncloud.com/server/developer_manual/core/apis/ocs-capabilities.html
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CommonOcsResponse<T>(
|
||||
val ocs: OCSResponse<T>
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OCSResponse<T>(
|
||||
val meta: MetaData,
|
||||
val data: T?
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MetaData(
|
||||
val status: String,
|
||||
@Json(name = "statuscode")
|
||||
val statusCode: Int,
|
||||
val message: String?,
|
||||
@Json(name = "itemsperpage")
|
||||
val itemsPerPage: String?,
|
||||
@Json(name = "totalitems")
|
||||
val totalItems: String?
|
||||
)
|
@ -0,0 +1,37 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* @author David González Verdugo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.resources
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
|
||||
/**
|
||||
* Facade to perform network calls without the verbosity of remote operations
|
||||
*/
|
||||
|
||||
interface Service {
|
||||
val client: OwnCloudClient
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2023 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.resources.appregistry
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.PostMethod
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Moshi
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.RequestBody
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class CreateRemoteFileWithAppProviderOperation(
|
||||
private val createFileWithAppProviderEndpoint: String,
|
||||
private val parentContainerId: String,
|
||||
private val filename: String,
|
||||
) : RemoteOperation<String>() {
|
||||
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
|
||||
return try {
|
||||
|
||||
val createFileWithAppProviderRequestBody = CreateFileWithAppProviderParams(parentContainerId, filename)
|
||||
.toRequestBody()
|
||||
|
||||
val stringUrl = client.baseUri.toString() + WebdavUtils.encodePath(createFileWithAppProviderEndpoint)
|
||||
|
||||
val postMethod = PostMethod(URL(stringUrl), createFileWithAppProviderRequestBody).apply {
|
||||
setReadTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
setConnectionTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
val status = client.executeHttpMethod(postMethod)
|
||||
Timber.d("Create file $filename with app provider in folder with ID $parentContainerId - $status${if (!isSuccess(status)) "(FAIL)" else ""}")
|
||||
|
||||
if (isSuccess(status)) RemoteOperationResult<String>(ResultCode.OK).apply {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val adapter: JsonAdapter<CreateFileWithAppProviderResponse> = moshi.adapter(CreateFileWithAppProviderResponse::class.java)
|
||||
|
||||
data = postMethod.getResponseBodyAsString()?.let { adapter.fromJson(it)!!.fileId }
|
||||
}
|
||||
else RemoteOperationResult<String>(postMethod).apply { data = "" }
|
||||
|
||||
} catch (e: Exception) {
|
||||
val result = RemoteOperationResult<String>(e)
|
||||
Timber.e(e, "Create file $filename with app provider in folder with ID $parentContainerId failed")
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
|
||||
|
||||
data class CreateFileWithAppProviderParams(
|
||||
val parentContainerId: String,
|
||||
val filename: String,
|
||||
) {
|
||||
fun toRequestBody(): RequestBody =
|
||||
FormBody.Builder()
|
||||
.add(PARAM_PARENT_CONTAINER_ID, parentContainerId)
|
||||
.add(PARAM_FILENAME, filename)
|
||||
.build()
|
||||
|
||||
companion object {
|
||||
const val PARAM_PARENT_CONTAINER_ID = "parent_container_id"
|
||||
const val PARAM_FILENAME = "filename"
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CreateFileWithAppProviderResponse(
|
||||
@Json(name = "file_id")
|
||||
val fileId: String,
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val TIMEOUT: Long = 5_000
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* @author Abel García de Prada
|
||||
*
|
||||
* Copyright (C) 2023 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.appregistry
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK
|
||||
import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
|
||||
class GetRemoteAppRegistryOperation(private val appUrl: String?) : RemoteOperation<AppRegistryResponse>() {
|
||||
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<AppRegistryResponse> {
|
||||
var result: RemoteOperationResult<AppRegistryResponse>
|
||||
|
||||
try {
|
||||
val uriBuilder = client.baseUri.buildUpon().apply {
|
||||
appendEncodedPath(appUrl)
|
||||
}
|
||||
val getMethod = GetMethod(URL(uriBuilder.build().toString()))
|
||||
val status = client.executeHttpMethod(getMethod)
|
||||
|
||||
val response = getMethod.getResponseBodyAsString()
|
||||
|
||||
if (status == HttpConstants.HTTP_OK) {
|
||||
Timber.d("Successful response $response")
|
||||
|
||||
// Parse the response
|
||||
val moshi: Moshi = Moshi.Builder().build()
|
||||
val adapter: JsonAdapter<AppRegistryResponse> = moshi.adapter(AppRegistryResponse::class.java)
|
||||
val appRegistryResponse: AppRegistryResponse = response?.let { adapter.fromJson(it) } ?: AppRegistryResponse(value = emptyList())
|
||||
|
||||
result = RemoteOperationResult(OK)
|
||||
result.data = appRegistryResponse
|
||||
|
||||
Timber.d("Get AppRegistry completed and parsed to ${result.data}")
|
||||
} else {
|
||||
result = RemoteOperationResult(getMethod)
|
||||
Timber.e("Failed response while getting app registry from the server status code: $status; response message: $response")
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
result = RemoteOperationResult(e)
|
||||
Timber.e(e, "Exception while getting app registry")
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2023 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.resources.appregistry
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.PostMethod
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.Moshi
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.RequestBody
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class GetUrlToOpenInWebRemoteOperation(
|
||||
private val openWithWebEndpoint: String,
|
||||
private val fileId: String,
|
||||
private val appName: String,
|
||||
) : RemoteOperation<String>() {
|
||||
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
|
||||
return try {
|
||||
|
||||
val openInWebRequestBody = OpenInWebParams(fileId, appName).toRequestBody()
|
||||
|
||||
val stringUrl =
|
||||
client.baseUri.toString() + WebdavUtils.encodePath(openWithWebEndpoint)
|
||||
|
||||
val postMethod = PostMethod(URL(stringUrl), openInWebRequestBody).apply {
|
||||
setReadTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
setConnectionTimeout(TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
val status = client.executeHttpMethod(postMethod)
|
||||
Timber.d("Open in web for file: $fileId - $status${if (!isSuccess(status)) "(FAIL)" else ""}")
|
||||
|
||||
if (isSuccess(status)) RemoteOperationResult<String>(ResultCode.OK).apply {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val adapter: JsonAdapter<OpenInWebResponse> = moshi.adapter(OpenInWebResponse::class.java)
|
||||
|
||||
data = postMethod.getResponseBodyAsString()?.let { adapter.fromJson(it)!!.uri }
|
||||
}
|
||||
else RemoteOperationResult<String>(postMethod).apply { data = "" }
|
||||
|
||||
} catch (e: Exception) {
|
||||
val result = RemoteOperationResult<String>(e)
|
||||
Timber.e(e, "Open in web for file: $fileId failed")
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
|
||||
|
||||
data class OpenInWebParams(
|
||||
val fileId: String,
|
||||
val appName: String,
|
||||
) {
|
||||
fun toRequestBody(): RequestBody =
|
||||
FormBody.Builder()
|
||||
.add(PARAM_FILE_ID, fileId)
|
||||
.add(PARAM_APP_NAME, appName)
|
||||
.build()
|
||||
|
||||
companion object {
|
||||
const val PARAM_FILE_ID = "file_id"
|
||||
const val PARAM_APP_NAME = "app_name"
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OpenInWebResponse(val uri: String)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Maximum time to wait for a response from the server in milliseconds.
|
||||
*/
|
||||
private const val TIMEOUT = 5_000L
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* @author Abel García de Prada
|
||||
*
|
||||
* Copyright (C) 2023 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.appregistry.responses
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class AppRegistryResponse(
|
||||
@Json(name = "mime-types")
|
||||
val value: List<AppRegistryMimeTypeResponse>
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class AppRegistryMimeTypeResponse(
|
||||
@Json(name = "mime_type") val mimeType: String,
|
||||
val ext: String? = null,
|
||||
@Json(name = "app_providers")
|
||||
val appProviders: List<AppRegistryProviderResponse>,
|
||||
val name: String? = null,
|
||||
val icon: String? = null,
|
||||
val description: String? = null,
|
||||
@Json(name = "allow_creation")
|
||||
val allowCreation: Boolean? = null,
|
||||
@Json(name = "default_application")
|
||||
val defaultApplication: String? = null
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class AppRegistryProviderResponse(
|
||||
val name: String,
|
||||
val icon: String,
|
||||
)
|
@ -1,5 +1,5 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
* Copyright (C) 2023 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -19,37 +19,26 @@
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.test_project.test;
|
||||
package com.owncloud.android.lib.resources.appregistry.services
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.resources.Service
|
||||
import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse
|
||||
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
|
||||
import com.owncloud.android.lib.test_project.R;
|
||||
interface AppRegistryService : Service {
|
||||
fun getAppRegistry(appUrl: String?): RemoteOperationResult<AppRegistryResponse>
|
||||
|
||||
public class Utils {
|
||||
|
||||
private static String LOG_TAG = Utils.class.getSimpleName();
|
||||
|
||||
private static String sBuildNumber = null;
|
||||
|
||||
public static void logAndThrow(String tag, RemoteOperationResult result) throws Exception {
|
||||
Log.e(tag, result.getLogMessage(), result.getException());
|
||||
throw new Exception(result.getLogMessage(), result.getException());
|
||||
}
|
||||
|
||||
public static String getBuildNumber(Context context) {
|
||||
if (sBuildNumber == null) {
|
||||
sBuildNumber = context.getString(R.string.build_number);
|
||||
if (sBuildNumber == null || sBuildNumber.length() == 0) {
|
||||
Log.w(LOG_TAG, "Build number unknown, using current time instead");
|
||||
sBuildNumber = Long.toString(System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
return sBuildNumber;
|
||||
}
|
||||
fun getUrlToOpenInWeb(
|
||||
openWebEndpoint: String,
|
||||
fileId: String,
|
||||
appName: String,
|
||||
): RemoteOperationResult<String>
|
||||
|
||||
fun createFileWithAppProvider(
|
||||
createFileWithAppProviderEndpoint: String,
|
||||
parentContainerId: String,
|
||||
filename: String,
|
||||
): RemoteOperationResult<String>
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2023 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.resources.appregistry.services
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.resources.appregistry.CreateRemoteFileWithAppProviderOperation
|
||||
import com.owncloud.android.lib.resources.appregistry.GetRemoteAppRegistryOperation
|
||||
import com.owncloud.android.lib.resources.appregistry.GetUrlToOpenInWebRemoteOperation
|
||||
import com.owncloud.android.lib.resources.appregistry.responses.AppRegistryResponse
|
||||
|
||||
class OCAppRegistryService(override val client: OwnCloudClient) : AppRegistryService {
|
||||
override fun getAppRegistry(appUrl: String?): RemoteOperationResult<AppRegistryResponse> =
|
||||
GetRemoteAppRegistryOperation(appUrl).execute(client)
|
||||
|
||||
override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String, appName: String): RemoteOperationResult<String> =
|
||||
GetUrlToOpenInWebRemoteOperation(
|
||||
openWithWebEndpoint = openWebEndpoint,
|
||||
fileId = fileId,
|
||||
appName = appName
|
||||
).execute(client)
|
||||
|
||||
override fun createFileWithAppProvider(
|
||||
createFileWithAppProviderEndpoint: String,
|
||||
parentContainerId: String,
|
||||
filename: String
|
||||
): RemoteOperationResult<String> =
|
||||
CreateRemoteFileWithAppProviderOperation(
|
||||
createFileWithAppProviderEndpoint = createFileWithAppProviderEndpoint,
|
||||
parentContainerId = parentContainerId,
|
||||
filename = filename,
|
||||
).execute(client)
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2023 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils.allPropSet
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Operation to check the existence of a path in a remote server.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* @author David González Verdugo
|
||||
* @author Abel García de Prada
|
||||
* @author Juan Carlos Garrote Gascón
|
||||
*
|
||||
* @param remotePath Path to append to the URL owned by the client instance.
|
||||
* @param isUserLoggedIn When `true`, the username won't be added at the end of the PROPFIND url since is not
|
||||
* needed to check user credentials
|
||||
*/
|
||||
class CheckPathExistenceRemoteOperation(
|
||||
val remotePath: String? = "",
|
||||
val isUserLoggedIn: Boolean,
|
||||
val spaceWebDavUrl: String? = null,
|
||||
) : RemoteOperation<Boolean>() {
|
||||
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<Boolean> {
|
||||
val baseStringUrl = spaceWebDavUrl ?: if (isUserLoggedIn) client.userFilesWebDavUri.toString() else client.baseFilesWebDavUri.toString()
|
||||
val stringUrl = if (isUserLoggedIn) baseStringUrl + WebdavUtils.encodePath(remotePath) else baseStringUrl
|
||||
|
||||
return try {
|
||||
val propFindMethod = PropfindMethod(URL(stringUrl), 0, allPropSet).apply {
|
||||
setReadTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
|
||||
setConnectionTimeout(TIMEOUT.toLong(), TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
val status = client.executeHttpMethod(propFindMethod)
|
||||
/* PROPFIND method
|
||||
* 404 NOT FOUND: path doesn't exist,
|
||||
* 207 MULTI_STATUS: path exists.
|
||||
*/
|
||||
Timber.d(
|
||||
"Existence check for $stringUrl finished with HTTP status $status${if (!isSuccess(status)) "(FAIL)" else ""}"
|
||||
)
|
||||
if (isSuccess(status)) RemoteOperationResult<Boolean>(ResultCode.OK).apply { data = true }
|
||||
else RemoteOperationResult<Boolean>(propFindMethod).apply { data = false }
|
||||
|
||||
} catch (e: Exception) {
|
||||
val result = RemoteOperationResult<Boolean>(e)
|
||||
Timber.e(
|
||||
e,
|
||||
"Existence check for $stringUrl : ${result.logMessage}"
|
||||
)
|
||||
result.data = false
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Maximum time to wait for a response from the server in milliseconds.
|
||||
*/
|
||||
private const val TIMEOUT = 10000
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2023 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.CopyMethod
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||
import com.owncloud.android.lib.common.utils.isOneOf
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Remote operation copying a remote file or folder in the ownCloud server to a different folder
|
||||
* in the same account.
|
||||
*
|
||||
* Allows renaming the copying file/folder at the same time.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* @author Christian Schabesberger
|
||||
* @author David González V.
|
||||
* @author Juan Carlos Garrote Gascón
|
||||
* @author Manuel Plazas Palacio
|
||||
*
|
||||
* @param sourceRemotePath Remote path of the file/folder to copy.
|
||||
* @param targetRemotePath Remote path desired for the file/folder to copy it.
|
||||
*/
|
||||
class CopyRemoteFileOperation(
|
||||
private val sourceRemotePath: String,
|
||||
private val targetRemotePath: String,
|
||||
private val sourceSpaceWebDavUrl: String? = null,
|
||||
private val targetSpaceWebDavUrl: String? = null,
|
||||
private val forceOverride: Boolean = false,
|
||||
) : RemoteOperation<String>() {
|
||||
|
||||
/**
|
||||
* Performs the rename operation.
|
||||
*
|
||||
* @param client Client object to communicate with the remote ownCloud server.
|
||||
*/
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<String> {
|
||||
if (targetRemotePath == sourceRemotePath && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) {
|
||||
// nothing to do!
|
||||
return RemoteOperationResult(ResultCode.OK)
|
||||
}
|
||||
if (targetRemotePath.startsWith(sourceRemotePath) && sourceSpaceWebDavUrl == targetSpaceWebDavUrl) {
|
||||
return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT)
|
||||
}
|
||||
|
||||
/// perform remote operation
|
||||
var result: RemoteOperationResult<String>
|
||||
try {
|
||||
val copyMethod = CopyMethod(
|
||||
url = URL((sourceSpaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(sourceRemotePath)),
|
||||
destinationUrl = (targetSpaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(targetRemotePath),
|
||||
forceOverride = forceOverride,
|
||||
).apply {
|
||||
addRequestHeaders(this)
|
||||
setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS)
|
||||
setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||
}
|
||||
val status = client.executeHttpMethod(copyMethod)
|
||||
when {
|
||||
isSuccess(status) -> {
|
||||
val fileRemoteId = copyMethod.getResponseHeader(HttpConstants.OC_FILE_REMOTE_ID)
|
||||
result = RemoteOperationResult(ResultCode.OK)
|
||||
result.setData(fileRemoteId)
|
||||
}
|
||||
|
||||
isPreconditionFailed(status) -> {
|
||||
result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE)
|
||||
client.exhaustResponse(copyMethod.getResponseBodyAsStream())
|
||||
|
||||
/// for other errors that could be explicitly handled, check first:
|
||||
/// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
|
||||
}
|
||||
|
||||
else -> {
|
||||
result = RemoteOperationResult(copyMethod)
|
||||
client.exhaustResponse(copyMethod.getResponseBodyAsStream())
|
||||
}
|
||||
}
|
||||
Timber.i("Copy $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
|
||||
} catch (e: Exception) {
|
||||
result = RemoteOperationResult(e)
|
||||
Timber.e(e, "Copy $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun addRequestHeaders(copyMethod: CopyMethod) {
|
||||
//Adding this because the library has an error with override
|
||||
if (copyMethod.forceOverride) {
|
||||
copyMethod.setRequestHeader(OVERWRITE, TRUE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
|
||||
|
||||
private fun isPreconditionFailed(status: Int) = status == HttpConstants.HTTP_PRECONDITION_FAILED
|
||||
|
||||
companion object {
|
||||
private const val COPY_READ_TIMEOUT = 10L
|
||||
private const val COPY_CONNECTION_TIMEOUT = 6L
|
||||
private const val OVERWRITE = "overwrite"
|
||||
private const val TRUE = "T"
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.MkColMethod
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Remote operation performing the creation of a new folder in the ownCloud server.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* @author masensio
|
||||
*
|
||||
* @param remotePath Full path to the new directory to create in the remote server.
|
||||
* @param createFullPath 'True' means that all the ancestor folders should be created.
|
||||
*/
|
||||
class CreateRemoteFolderOperation(
|
||||
val remotePath: String,
|
||||
private val createFullPath: Boolean,
|
||||
private val isChunksFolder: Boolean = false,
|
||||
val spaceWebDavUrl: String? = null,
|
||||
) : RemoteOperation<Unit>() {
|
||||
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||
|
||||
var result = createFolder(client)
|
||||
if (!result.isSuccess && createFullPath && result.code == ResultCode.CONFLICT) {
|
||||
result = createParentFolder(FileUtils.getParentPath(remotePath), client)
|
||||
|
||||
if (result.isSuccess) {
|
||||
// Second and last try
|
||||
result = createFolder(client)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun createFolder(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||
var result: RemoteOperationResult<Unit>
|
||||
try {
|
||||
val webDavUri = if (isChunksFolder) {
|
||||
client.uploadsWebDavUri.toString()
|
||||
} else {
|
||||
spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
|
||||
}
|
||||
|
||||
val mkCol = MkColMethod(
|
||||
URL(webDavUri + WebdavUtils.encodePath(remotePath))
|
||||
).apply {
|
||||
setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
|
||||
setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
val status = client.executeHttpMethod(mkCol)
|
||||
result =
|
||||
if (status == HttpConstants.HTTP_CREATED) {
|
||||
RemoteOperationResult(ResultCode.OK)
|
||||
} else {
|
||||
RemoteOperationResult(mkCol)
|
||||
}
|
||||
|
||||
Timber.d("Create directory $remotePath: ${result.logMessage}")
|
||||
client.exhaustResponse(mkCol.getResponseBodyAsStream())
|
||||
|
||||
} catch (e: Exception) {
|
||||
result = RemoteOperationResult(e)
|
||||
Timber.e(e, "Create directory $remotePath: ${result.logMessage}")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun createParentFolder(parentPath: String, client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||
val operation: RemoteOperation<Unit> = CreateRemoteFolderOperation(parentPath, createFullPath)
|
||||
return operation.execute(client)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val READ_TIMEOUT: Long = 30_000
|
||||
private const val CONNECTION_TIMEOUT: Long = 5_000
|
||||
}
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod
|
||||
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.OperationCancelledException
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import timber.log.Timber
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* Remote operation performing the download of a remote file in the ownCloud server.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* @author masensio
|
||||
*/
|
||||
class DownloadRemoteFileOperation(
|
||||
private val remotePath: String,
|
||||
localFolderPath: String,
|
||||
private val spaceWebDavUrl: String? = null,
|
||||
) : RemoteOperation<Unit>() {
|
||||
|
||||
private val cancellationRequested = AtomicBoolean(false)
|
||||
private val dataTransferListeners: MutableSet<OnDatatransferProgressListener> = HashSet()
|
||||
|
||||
var modificationTimestamp: Long = 0
|
||||
private set
|
||||
|
||||
var etag: String = ""
|
||||
private set
|
||||
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||
// download will be performed to a temporal file, then moved to the final location
|
||||
val tmpFile = File(tmpPath)
|
||||
|
||||
// perform the download
|
||||
return try {
|
||||
tmpFile.parentFile?.mkdirs()
|
||||
downloadFile(client, tmpFile).also { result ->
|
||||
Timber.i("Download of $remotePath to $tmpPath: ${result.logMessage}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
RemoteOperationResult<Unit>(e).also { result ->
|
||||
Timber.e(e, "Download of $remotePath to $tmpPath: ${result.logMessage}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
private fun downloadFile(client: OwnCloudClient, targetFile: File): RemoteOperationResult<Unit> {
|
||||
val result: RemoteOperationResult<Unit>
|
||||
var it: Iterator<OnDatatransferProgressListener>
|
||||
var fos: FileOutputStream? = null
|
||||
var bis: BufferedInputStream? = null
|
||||
var savedFile = false
|
||||
|
||||
val webDavUri = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
|
||||
val getMethod = GetMethod(URL(webDavUri + WebdavUtils.encodePath(remotePath)))
|
||||
|
||||
try {
|
||||
val status = client.executeHttpMethod(getMethod)
|
||||
|
||||
if (isSuccess(status)) {
|
||||
targetFile.createNewFile()
|
||||
bis = BufferedInputStream(getMethod.getResponseBodyAsStream())
|
||||
fos = FileOutputStream(targetFile)
|
||||
var transferred: Long = 0
|
||||
val contentLength = getMethod.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER)
|
||||
val totalToTransfer = if (!contentLength.isNullOrEmpty()) {
|
||||
contentLength.toLong()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
val bytes = ByteArray(4096)
|
||||
var readResult: Int
|
||||
while (bis.read(bytes).also { readResult = it } != -1) {
|
||||
synchronized(cancellationRequested) {
|
||||
if (cancellationRequested.get()) {
|
||||
getMethod.abort()
|
||||
throw OperationCancelledException()
|
||||
}
|
||||
}
|
||||
fos.write(bytes, 0, readResult)
|
||||
transferred += readResult.toLong()
|
||||
synchronized(dataTransferListeners) {
|
||||
it = dataTransferListeners.iterator()
|
||||
while (it.hasNext()) {
|
||||
it.next()
|
||||
.onTransferProgress(readResult.toLong(), transferred, totalToTransfer, targetFile.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (transferred == totalToTransfer) { // Check if the file is completed
|
||||
savedFile = true
|
||||
val modificationTime =
|
||||
getMethod.getResponseHeaders()?.get("Last-Modified")
|
||||
?: getMethod.getResponseHeader("last-modified")
|
||||
|
||||
if (modificationTime != null) {
|
||||
val modificationDate = WebdavUtils.parseResponseDate(modificationTime)
|
||||
modificationTimestamp = modificationDate?.time ?: 0
|
||||
} else {
|
||||
Timber.e("Could not read modification time from response downloading %s", remotePath)
|
||||
}
|
||||
etag = WebdavUtils.getEtagFromResponse(getMethod)
|
||||
|
||||
// Get rid of extra quotas
|
||||
etag = etag.replace("\"", "")
|
||||
if (etag.isEmpty()) {
|
||||
Timber.e("Could not read eTag from response downloading %s", remotePath)
|
||||
}
|
||||
} else {
|
||||
Timber.e("Content-Length not equal to transferred bytes.")
|
||||
Timber.d("totalToTransfer = $totalToTransfer, transferred = $transferred")
|
||||
client.exhaustResponse(getMethod.getResponseBodyAsStream())
|
||||
// TODO some kind of error control!
|
||||
}
|
||||
|
||||
} else if (status != HttpConstants.HTTP_FORBIDDEN && status != HttpConstants.HTTP_SERVICE_UNAVAILABLE) {
|
||||
client.exhaustResponse(getMethod.getResponseBodyAsStream())
|
||||
} // else, body read by RemoteOperationResult constructor
|
||||
|
||||
result =
|
||||
if (isSuccess(status)) {
|
||||
RemoteOperationResult(RemoteOperationResult.ResultCode.OK)
|
||||
} else {
|
||||
RemoteOperationResult(getMethod)
|
||||
}
|
||||
} finally {
|
||||
fos?.close()
|
||||
bis?.close()
|
||||
if (!savedFile && targetFile.exists()) {
|
||||
targetFile.delete()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK
|
||||
|
||||
private val tmpPath: String = localFolderPath + remotePath
|
||||
|
||||
fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) {
|
||||
synchronized(dataTransferListeners) { dataTransferListeners.add(listener) }
|
||||
}
|
||||
|
||||
fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener?) {
|
||||
synchronized(dataTransferListeners) { dataTransferListeners.remove(listener) }
|
||||
}
|
||||
|
||||
fun cancel() {
|
||||
cancellationRequested.set(true) // atomic set; there is no need of synchronizing it
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.resources.files;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class FileUtils {
|
||||
public static final String FINAL_CHUNKS_FILE = ".file";
|
||||
public static final String MIME_DIR = "DIR";
|
||||
public static final String MIME_DIR_UNIX = "httpd/unix-directory";
|
||||
public static final String MODE_READ_ONLY = "r";
|
||||
|
||||
static String getParentPath(String remotePath) {
|
||||
String parentPath = new File(remotePath).getParent();
|
||||
parentPath = parentPath.endsWith(File.separator) ? parentPath : parentPath + File.separator;
|
||||
return parentPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the fileName to detect if contains any forbidden character: / , \ , < , > ,
|
||||
* : , " , | , ? , *
|
||||
*
|
||||
*/
|
||||
public static boolean isValidName(String fileName) {
|
||||
boolean result = true;
|
||||
|
||||
Timber.d("fileName =======%s", fileName);
|
||||
if (fileName.contains(File.separator)) {
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2022 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Operation to get the base url, which might differ in case of a redirect.
|
||||
*
|
||||
* @author Christian Schabesberger
|
||||
*/
|
||||
|
||||
class GetBaseUrlRemoteOperation : RemoteOperation<String?>() {
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<String?> {
|
||||
return try {
|
||||
val stringUrl = client.baseFilesWebDavUri.toString()
|
||||
|
||||
val propFindMethod = PropfindMethod(URL(stringUrl), 0, DavUtils.allPropSet).apply {
|
||||
setReadTimeout(TIMEOUT, TimeUnit.SECONDS)
|
||||
setConnectionTimeout(TIMEOUT, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
val status = client.executeHttpMethod(propFindMethod)
|
||||
|
||||
if (isSuccess(status)) {
|
||||
RemoteOperationResult<String?>(RemoteOperationResult.ResultCode.OK).apply {
|
||||
data = propFindMethod.getFinalUrl().toString()
|
||||
}
|
||||
} else {
|
||||
RemoteOperationResult<String?>(propFindMethod).apply {
|
||||
data = null
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Could not get actuall (or redirected) base URL from base url (/).")
|
||||
RemoteOperationResult<String?>(e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_MULTI_STATUS
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Maximum time to wait for a response from the server in milliseconds.
|
||||
*/
|
||||
private const val TIMEOUT = 10_000L
|
||||
}
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2023 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import android.net.Uri
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||
import com.owncloud.android.lib.common.utils.isOneOf
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Remote operation moving a remote file or folder in the ownCloud server to a different folder
|
||||
* in the same account and space.
|
||||
*
|
||||
* Allows renaming the moving file/folder at the same time.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* @author David González Verdugo
|
||||
* @author Abel García de Prada
|
||||
* @author Juan Carlos Garrote Gascón
|
||||
* @author Manuel Plazas Palacio
|
||||
*
|
||||
* @param sourceRemotePath Remote path of the file/folder to copy.
|
||||
* @param targetRemotePath Remote path desired for the file/folder to copy it.
|
||||
*/
|
||||
open class MoveRemoteFileOperation(
|
||||
private val sourceRemotePath: String,
|
||||
private val targetRemotePath: String,
|
||||
private val spaceWebDavUrl: String? = null,
|
||||
private val forceOverride: Boolean = false,
|
||||
) : RemoteOperation<Unit>() {
|
||||
|
||||
/**
|
||||
* Performs the rename operation.
|
||||
*
|
||||
* @param client Client object to communicate with the remote ownCloud server.
|
||||
*/
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||
if (targetRemotePath == sourceRemotePath) {
|
||||
// nothing to do!
|
||||
return RemoteOperationResult(ResultCode.OK)
|
||||
}
|
||||
|
||||
if (targetRemotePath.startsWith(sourceRemotePath)) {
|
||||
return RemoteOperationResult(ResultCode.INVALID_MOVE_INTO_DESCENDANT)
|
||||
}
|
||||
|
||||
/// perform remote operation
|
||||
var result: RemoteOperationResult<Unit>
|
||||
try {
|
||||
// After finishing a chunked upload, we have to move the resulting file from uploads folder to files one,
|
||||
// so this uri has to be customizable
|
||||
val srcWebDavUri = getSrcWebDavUriForClient(client)
|
||||
val moveMethod = MoveMethod(
|
||||
url = URL((spaceWebDavUrl ?: srcWebDavUri.toString()) + WebdavUtils.encodePath(sourceRemotePath)),
|
||||
destinationUrl = (spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(targetRemotePath),
|
||||
forceOverride = forceOverride,
|
||||
).apply {
|
||||
addRequestHeaders(this)
|
||||
setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS)
|
||||
setConnectionTimeout(MOVE_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
val status = client.executeHttpMethod(moveMethod)
|
||||
|
||||
when {
|
||||
isSuccess(status) -> {
|
||||
result = RemoteOperationResult<Unit>(ResultCode.OK)
|
||||
}
|
||||
|
||||
isPreconditionFailed(status) -> {
|
||||
result = RemoteOperationResult<Unit>(ResultCode.INVALID_OVERWRITE)
|
||||
client.exhaustResponse(moveMethod.getResponseBodyAsStream())
|
||||
|
||||
/// for other errors that could be explicitly handled, check first:
|
||||
/// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4
|
||||
}
|
||||
|
||||
else -> {
|
||||
result = RemoteOperationResult<Unit>(moveMethod)
|
||||
client.exhaustResponse(moveMethod.getResponseBodyAsStream())
|
||||
}
|
||||
}
|
||||
|
||||
Timber.i("Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
|
||||
} catch (e: Exception) {
|
||||
result = RemoteOperationResult<Unit>(e)
|
||||
Timber.e(e, "Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}")
|
||||
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* For standard moves, we will use [OwnCloudClient.getUserFilesWebDavUri].
|
||||
* In case we need a different source Uri, override this method.
|
||||
*/
|
||||
open fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.userFilesWebDavUri
|
||||
|
||||
/**
|
||||
* For standard moves, we won't need any special headers.
|
||||
* In case new headers are needed, override this method
|
||||
*/
|
||||
open fun addRequestHeaders(moveMethod: MoveMethod) {
|
||||
//Adding this because the library has an error with override
|
||||
if (moveMethod.forceOverride) {
|
||||
moveMethod.setRequestHeader(OVERWRITE, TRUE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
|
||||
|
||||
private fun isPreconditionFailed(status: Int) = status == HttpConstants.HTTP_PRECONDITION_FAILED
|
||||
|
||||
companion object {
|
||||
private const val MOVE_READ_TIMEOUT = 10L
|
||||
private const val MOVE_CONNECTION_TIMEOUT = 6L
|
||||
private const val OVERWRITE = "overwrite"
|
||||
private const val TRUE = "T"
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2016 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_MULTI_STATUS
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.DavConstants.DEPTH_0
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.utils.isOneOf
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Remote operation performing the read a file from the ownCloud server.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* @author masensio
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
|
||||
class ReadRemoteFileOperation(
|
||||
val remotePath: String,
|
||||
val spaceWebDavUrl: String? = null,
|
||||
) : RemoteOperation<RemoteFile>() {
|
||||
|
||||
/**
|
||||
* Performs the read operation.
|
||||
*
|
||||
* @param client Client object to communicate with the remote ownCloud server.
|
||||
*/
|
||||
@Override
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<RemoteFile> {
|
||||
try {
|
||||
val propFind = PropfindMethod(
|
||||
url = getFinalWebDavUrl(),
|
||||
depth = DEPTH_0,
|
||||
propertiesToRequest = DavUtils.allPropSet
|
||||
).apply {
|
||||
setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS)
|
||||
setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
val status = client.executeHttpMethod(propFind)
|
||||
Timber.i("Read remote file $remotePath with status ${propFind.statusCode}")
|
||||
|
||||
return if (isSuccess(status)) {
|
||||
val remoteFile = RemoteFile.getRemoteFileFromDav(
|
||||
davResource = propFind.root!!,
|
||||
userId = AccountUtils.getUserId(mAccount, mContext),
|
||||
userName = mAccount.name,
|
||||
spaceWebDavUrl = spaceWebDavUrl,
|
||||
)
|
||||
|
||||
RemoteOperationResult<RemoteFile>(RemoteOperationResult.ResultCode.OK).apply {
|
||||
data = remoteFile
|
||||
}
|
||||
} else {
|
||||
RemoteOperationResult<RemoteFile>(propFind).also {
|
||||
client.exhaustResponse(propFind.getResponseBodyAsStream())
|
||||
}
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
return RemoteOperationResult(exception)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFinalWebDavUrl(): URL {
|
||||
val baseWebDavUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
|
||||
|
||||
return URL(baseWebDavUrl + WebdavUtils.encodePath(remotePath))
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int) = status.isOneOf(HTTP_MULTI_STATUS, HTTP_OK)
|
||||
|
||||
companion object {
|
||||
private const val SYNC_READ_TIMEOUT = 40_000L
|
||||
private const val SYNC_CONNECTION_TIMEOUT = 5_000L
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import at.bitfire.dav4jvm.PropertyRegistry
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.accounts.AccountUtils
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_MULTI_STATUS
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.DavConstants
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.DavUtils
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||
import com.owncloud.android.lib.common.utils.isOneOf
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Remote operation performing the read of remote file or folder in the ownCloud server.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* @author masensio
|
||||
* @author David González Verdugo
|
||||
*/
|
||||
class ReadRemoteFolderOperation(
|
||||
val remotePath: String,
|
||||
val spaceWebDavUrl: String? = null,
|
||||
) : RemoteOperation<ArrayList<RemoteFile>>() {
|
||||
|
||||
/**
|
||||
* Performs the read operation.
|
||||
*
|
||||
* @param client Client object to communicate with the remote ownCloud server.
|
||||
*/
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<ArrayList<RemoteFile>> {
|
||||
try {
|
||||
PropertyRegistry.register(OCShareTypes.Factory())
|
||||
|
||||
val propfindMethod = PropfindMethod(
|
||||
getFinalWebDavUrl(),
|
||||
DavConstants.DEPTH_1,
|
||||
DavUtils.allPropSet
|
||||
)
|
||||
|
||||
val status = client.executeHttpMethod(propfindMethod)
|
||||
|
||||
if (isSuccess(status)) {
|
||||
val mFolderAndFiles = ArrayList<RemoteFile>()
|
||||
|
||||
val remoteFolder = RemoteFile.getRemoteFileFromDav(
|
||||
davResource = propfindMethod.root!!,
|
||||
userId = AccountUtils.getUserId(mAccount, mContext),
|
||||
userName = mAccount.name,
|
||||
spaceWebDavUrl = spaceWebDavUrl,
|
||||
)
|
||||
mFolderAndFiles.add(remoteFolder)
|
||||
|
||||
// loop to update every child
|
||||
propfindMethod.members.forEach { resource ->
|
||||
val remoteFile = RemoteFile.getRemoteFileFromDav(
|
||||
davResource = resource,
|
||||
userId = AccountUtils.getUserId(mAccount, mContext),
|
||||
userName = mAccount.name,
|
||||
spaceWebDavUrl = spaceWebDavUrl,
|
||||
)
|
||||
mFolderAndFiles.add(remoteFile)
|
||||
}
|
||||
|
||||
// Result of the operation
|
||||
return RemoteOperationResult<ArrayList<RemoteFile>>(ResultCode.OK).apply {
|
||||
data = mFolderAndFiles
|
||||
Timber.i("Synchronized $remotePath with ${mFolderAndFiles.size} files. ${this.logMessage}")
|
||||
}
|
||||
} else { // synchronization failed
|
||||
return RemoteOperationResult<ArrayList<RemoteFile>>(propfindMethod).also {
|
||||
Timber.w("Synchronized $remotePath ${it.logMessage}")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
return RemoteOperationResult<ArrayList<RemoteFile>>(e).also {
|
||||
Timber.e(it.exception, "Synchronized $remotePath")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFinalWebDavUrl(): URL {
|
||||
val baseWebDavUrl = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
|
||||
|
||||
return URL(baseWebDavUrl + WebdavUtils.encodePath(remotePath))
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int): Boolean = status.isOneOf(HTTP_OK, HTTP_MULTI_STATUS)
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import at.bitfire.dav4jvm.PropStat
|
||||
import at.bitfire.dav4jvm.Property
|
||||
import at.bitfire.dav4jvm.Response
|
||||
import at.bitfire.dav4jvm.property.CreationDate
|
||||
import at.bitfire.dav4jvm.property.GetContentLength
|
||||
import at.bitfire.dav4jvm.property.GetContentType
|
||||
import at.bitfire.dav4jvm.property.GetETag
|
||||
import at.bitfire.dav4jvm.property.GetLastModified
|
||||
import at.bitfire.dav4jvm.property.OCId
|
||||
import at.bitfire.dav4jvm.property.OCPermissions
|
||||
import at.bitfire.dav4jvm.property.OCPrivatelink
|
||||
import at.bitfire.dav4jvm.property.OCSize
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes
|
||||
import com.owncloud.android.lib.common.utils.isOneOf
|
||||
import com.owncloud.android.lib.resources.shares.ShareType
|
||||
import com.owncloud.android.lib.resources.shares.ShareType.Companion.fromValue
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import okhttp3.HttpUrl
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Contains the data of a Remote File from a WebDavEntry
|
||||
*
|
||||
* The path received must be URL-decoded. Path separator must be File.separator, and it must be the first character in 'path'.
|
||||
*
|
||||
* @author masensio
|
||||
* @author Christian Schabesberger
|
||||
* @author Abel García de Prada
|
||||
*/
|
||||
@Parcelize
|
||||
data class RemoteFile(
|
||||
var remotePath: String,
|
||||
var mimeType: String = "DIR",
|
||||
var length: Long = 0,
|
||||
var creationTimestamp: Long = 0,
|
||||
var modifiedTimestamp: Long = 0,
|
||||
var etag: String? = null,
|
||||
var permissions: String? = null,
|
||||
var remoteId: String? = null,
|
||||
var size: Long = 0,
|
||||
var privateLink: String? = null,
|
||||
var owner: String,
|
||||
var sharedByLink: Boolean = false,
|
||||
var sharedWithSharee: Boolean = false,
|
||||
) : Parcelable {
|
||||
|
||||
// TODO: Quotas not used. Use or remove them.
|
||||
init {
|
||||
require(
|
||||
!(remotePath.isEmpty() || !remotePath.startsWith(File.separator))
|
||||
) { "Trying to create a OCFile with a non valid remote path: $remotePath" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to find out if this file is a folder.
|
||||
*
|
||||
* @return true if it is a folder
|
||||
*/
|
||||
val isFolder
|
||||
get() = mimeType.isOneOf(MIME_DIR, MIME_DIR_UNIX)
|
||||
|
||||
companion object {
|
||||
|
||||
const val MIME_DIR = "DIR"
|
||||
const val MIME_DIR_UNIX = "httpd/unix-directory"
|
||||
|
||||
fun getRemoteFileFromDav(
|
||||
davResource: Response,
|
||||
userId: String,
|
||||
userName: String,
|
||||
spaceWebDavUrl: String? = null
|
||||
): RemoteFile {
|
||||
val remotePath = getRemotePathFromUrl(davResource.href, userId, spaceWebDavUrl)
|
||||
val remoteFile = RemoteFile(remotePath = remotePath, owner = userName)
|
||||
val properties = getPropertiesEvenIfPostProcessing(davResource)
|
||||
|
||||
for (property in properties) {
|
||||
when (property) {
|
||||
is CreationDate -> {
|
||||
remoteFile.creationTimestamp = property.creationDate.toLong()
|
||||
}
|
||||
is GetContentLength -> {
|
||||
remoteFile.length = property.contentLength
|
||||
}
|
||||
is GetContentType -> {
|
||||
property.type?.let { remoteFile.mimeType = it }
|
||||
}
|
||||
is GetLastModified -> {
|
||||
remoteFile.modifiedTimestamp = property.lastModified
|
||||
}
|
||||
is GetETag -> {
|
||||
remoteFile.etag = property.eTag
|
||||
}
|
||||
is OCPermissions -> {
|
||||
remoteFile.permissions = property.permission
|
||||
}
|
||||
is OCId -> {
|
||||
remoteFile.remoteId = property.id
|
||||
}
|
||||
is OCSize -> {
|
||||
remoteFile.size = property.size
|
||||
}
|
||||
is OCPrivatelink -> {
|
||||
remoteFile.privateLink = property.link
|
||||
}
|
||||
is OCShareTypes -> {
|
||||
val list = property.shareTypes
|
||||
for (i in list.indices) {
|
||||
val shareType = fromValue(list[i].toInt())
|
||||
if (shareType == null) {
|
||||
Timber.d("Illegal share type value: " + list[i])
|
||||
continue
|
||||
}
|
||||
if (shareType == ShareType.PUBLIC_LINK) {
|
||||
remoteFile.sharedByLink = true
|
||||
} else if (shareType == ShareType.USER || shareType == ShareType.FEDERATED || shareType == ShareType.GROUP) {
|
||||
remoteFile.sharedWithSharee = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return remoteFile
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a relative path from a remote file url
|
||||
*
|
||||
*
|
||||
* Example legacy:
|
||||
* /remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt
|
||||
*
|
||||
* Example spaces:
|
||||
* /dav/spaces/8871f4f3-fc6f-4a66-8bed-62f175f76f38$05bca744-d89f-4e9c-a990-25a0d7f03fe9/Documents/text.txt => /Documents/text.txt
|
||||
*
|
||||
* @param url remote file url
|
||||
* @param userId file owner
|
||||
* @param spaceWebDavUrl custom web dav url for space
|
||||
* @return remote relative path of the file
|
||||
*/
|
||||
@VisibleForTesting
|
||||
fun getRemotePathFromUrl(
|
||||
url: HttpUrl,
|
||||
userId: String,
|
||||
spaceWebDavUrl: String? = null,
|
||||
): String {
|
||||
val davFilesPath = spaceWebDavUrl ?: (OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId)
|
||||
val absoluteDavPath = if (spaceWebDavUrl != null) Uri.decode(url.toString()) else Uri.decode(url.encodedPath)
|
||||
val pathToOc = absoluteDavPath.split(davFilesPath).first()
|
||||
return absoluteDavPath.replace(pathToOc + davFilesPath, "")
|
||||
}
|
||||
|
||||
private fun getPropertiesEvenIfPostProcessing(response: Response): List<Property> {
|
||||
return if (response.isSuccess())
|
||||
response.propstat.filter { propStat -> propStat.isSuccessOrPostProcessing() }.map { it.properties }.flatten()
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
|
||||
private fun PropStat.isSuccessOrPostProcessing() = (status.code / 100 == 2 || status.code == HttpConstants.HTTP_TOO_EARLY)
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2020 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_NO_CONTENT
|
||||
import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK
|
||||
import com.owncloud.android.lib.common.http.methods.nonwebdav.DeleteMethod
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||
import com.owncloud.android.lib.common.utils.isOneOf
|
||||
import timber.log.Timber
|
||||
import java.net.URL
|
||||
|
||||
/**
|
||||
* Remote operation performing the removal of a remote file or folder in the ownCloud server.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* @author masensio
|
||||
* @author David González Verdugo
|
||||
* @author Abel García de Prada
|
||||
*/
|
||||
open class RemoveRemoteFileOperation(
|
||||
private val remotePath: String,
|
||||
val spaceWebDavUrl: String? = null,
|
||||
) : RemoteOperation<Unit>() {
|
||||
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||
var result: RemoteOperationResult<Unit>
|
||||
try {
|
||||
val srcWebDavUri = getSrcWebDavUriForClient(client)
|
||||
val deleteMethod = DeleteMethod(
|
||||
URL(srcWebDavUri + WebdavUtils.encodePath(remotePath))
|
||||
)
|
||||
val status = client.executeHttpMethod(deleteMethod)
|
||||
|
||||
result = if (isSuccess(status)) {
|
||||
RemoteOperationResult<Unit>(ResultCode.OK)
|
||||
} else {
|
||||
RemoteOperationResult<Unit>(deleteMethod)
|
||||
}
|
||||
Timber.i("Remove $remotePath: ${result.logMessage}")
|
||||
} catch (e: Exception) {
|
||||
result = RemoteOperationResult<Unit>(e)
|
||||
Timber.e(e, "Remove $remotePath: ${result.logMessage}")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* For standard removals, we will use [OwnCloudClient.getUserFilesWebDavUri].
|
||||
* In case we need a different source Uri, override this method.
|
||||
*/
|
||||
open fun getSrcWebDavUriForClient(client: OwnCloudClient): String = spaceWebDavUrl ?: client.userFilesWebDavUri.toString()
|
||||
|
||||
private fun isSuccess(status: Int) = status.isOneOf(HTTP_OK, HTTP_NO_CONTENT)
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/* ownCloud Android Library is available under MIT license
|
||||
* Copyright (C) 2021 ownCloud GmbH.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.owncloud.android.lib.resources.files
|
||||
|
||||
import com.owncloud.android.lib.common.OwnCloudClient
|
||||
import com.owncloud.android.lib.common.http.HttpConstants
|
||||
import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod
|
||||
import com.owncloud.android.lib.common.network.WebdavUtils
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperation
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult
|
||||
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode
|
||||
import com.owncloud.android.lib.common.utils.isOneOf
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Remote operation performing the rename of a remote file or folder in the ownCloud server.
|
||||
*
|
||||
* @author David A. Velasco
|
||||
* @author masensio
|
||||
*/
|
||||
class RenameRemoteFileOperation(
|
||||
private val oldName: String,
|
||||
private val oldRemotePath: String,
|
||||
private val newName: String,
|
||||
isFolder: Boolean,
|
||||
val spaceWebDavUrl: String? = null,
|
||||
) : RemoteOperation<Unit>() {
|
||||
|
||||
private var newRemotePath: String
|
||||
|
||||
init {
|
||||
var parent = (File(oldRemotePath)).parent ?: throw IllegalArgumentException()
|
||||
if (!parent.endsWith(File.separator)) {
|
||||
parent = parent.plus(File.separator)
|
||||
}
|
||||
newRemotePath = parent.plus(newName)
|
||||
if (isFolder) {
|
||||
newRemotePath.plus(File.separator)
|
||||
}
|
||||
}
|
||||
|
||||
override fun run(client: OwnCloudClient): RemoteOperationResult<Unit> {
|
||||
var result: RemoteOperationResult<Unit>
|
||||
try {
|
||||
if (newName == oldName) {
|
||||
return RemoteOperationResult<Unit>(ResultCode.OK)
|
||||
}
|
||||
|
||||
if (targetPathIsUsed(client)) {
|
||||
return RemoteOperationResult<Unit>(ResultCode.INVALID_OVERWRITE)
|
||||
}
|
||||
|
||||
val moveMethod: MoveMethod = MoveMethod(
|
||||
url = URL((spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(oldRemotePath)),
|
||||
destinationUrl = (spaceWebDavUrl ?: client.userFilesWebDavUri.toString()) + WebdavUtils.encodePath(newRemotePath),
|
||||
).apply {
|
||||
setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
setConnectionTimeout(RENAME_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
val status = client.executeHttpMethod(moveMethod)
|
||||
|
||||
result = if (isSuccess(status)) {
|
||||
RemoteOperationResult<Unit>(ResultCode.OK)
|
||||
} else {
|
||||
RemoteOperationResult<Unit>(moveMethod)
|
||||
}
|
||||
|
||||
Timber.i("Rename $oldRemotePath to $newRemotePath: ${result.logMessage}")
|
||||
client.exhaustResponse(moveMethod.getResponseBodyAsStream())
|
||||
return result
|
||||
} catch (exception: Exception) {
|
||||
result = RemoteOperationResult<Unit>(exception)
|
||||
Timber.e(exception, "Rename $oldRemotePath to $newName: ${result.logMessage}")
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a file with the new name already exists.
|
||||
*
|
||||
* @return 'True' if the target path is already used by an existing file.
|
||||
*/
|
||||
private fun targetPathIsUsed(client: OwnCloudClient): Boolean {
|
||||
val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation(newRemotePath, true)
|
||||
val exists = checkPathExistenceRemoteOperation.execute(client)
|
||||
return exists.isSuccess
|
||||
}
|
||||
|
||||
private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT)
|
||||
|
||||
companion object {
|
||||
private const val RENAME_READ_TIMEOUT = 10_000L
|
||||
private const val RENAME_CONNECTION_TIMEOUT = 5_000L
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user