Compare commits
635 Commits
stable21
...
v6.0.4beta1
| Author | SHA1 | Date | |
|---|---|---|---|
| 508d84dcd8 | |||
| 659a3d4a37 | |||
| 3d26896431 | |||
| f6c970e214 | |||
| 84222e30a7 | |||
| 81fc7cfb21 | |||
| 04817fb0f3 | |||
| bca536eb3d | |||
| 64534546be | |||
| 00ec5fc193 | |||
| 8ef1542ad0 | |||
| eeca726d28 | |||
| e793a87954 | |||
| 6e0a218d11 | |||
| b2fae8a8b7 | |||
| 193d237b56 | |||
| 4bc091965f | |||
| 3ea94a7930 | |||
| 129bfad204 | |||
| 4d7e1c568c | |||
| f36e354a42 | |||
| 81f6e78c0c | |||
| a543f46699 | |||
| 2895261102 | |||
| 2911906586 | |||
| 909913d581 | |||
| edced60ed4 | |||
| 45d165405f | |||
| e98627d6ab | |||
| a4c422a6da | |||
| 6ec29711fc | |||
| 49f2955119 | |||
| b62ba9808a | |||
| f814b9a23c | |||
| b3c8b574df | |||
| 79b953a920 | |||
| d3445d9668 | |||
| e5dcec75ba | |||
| a908e281e7 | |||
| 2910486979 | |||
| 58b6b89ec4 | |||
| 8758b66fc4 | |||
| 8429dde4d9 | |||
| 37ef48d9f4 | |||
| 776d656ea9 | |||
| d17335d7a8 | |||
| 84b239f123 | |||
| ab3b713c23 | |||
| 1eedab7f6e | |||
| 9a5da34721 | |||
| 6f29e1c67b | |||
| 516f75fc6b | |||
| 9f405339d6 | |||
| 419e0354d5 | |||
| 313324a7b4 | |||
| c18f2943d5 | |||
| 0c355cb1fb | |||
| 68434510bd | |||
| 1e6b4576c2 | |||
| 8c1eff8f52 | |||
| 37e29169e0 | |||
| b91e3be5de | |||
| 113cd404c0 | |||
| d2dbab21fb | |||
| a152f3a50f | |||
| c341a971f8 | |||
| 1d2dc9b685 | |||
| 4e1d57e815 | |||
| 76cdd85cd9 | |||
| 6f8a24c2cd | |||
| 835025e569 | |||
| 821ad6ad41 | |||
| 3fe45b1d4c | |||
| 86757cdb45 | |||
| 65ea07fc54 | |||
| a3d93f0ba1 | |||
| 283a8b9624 | |||
| dacacacc4f | |||
| 4f9a5c9d3c | |||
| e5def25f40 | |||
| 2c92ef9e64 | |||
| 16ae62f246 | |||
| 53df9c93c4 | |||
| 14c95086a9 | |||
| 4696b06060 | |||
| 0a9487ec80 | |||
| fcffccf018 | |||
| 72b90816fa | |||
| 6aeb5996b6 | |||
| af45da94b3 | |||
| f93ab1105f | |||
| 637aa56197 | |||
| 649233e54c | |||
| 8d00f1ca1f | |||
| 60edf98c15 | |||
| 1041617a4e | |||
| a314508543 | |||
| 74241140d2 | |||
| d3a15b6b07 | |||
| 1403a85950 | |||
| 130f2419f5 | |||
| a53ec9ebeb | |||
| 33bdab1618 | |||
| 3405342ab6 | |||
| 980b11f096 | |||
| 22485fe743 | |||
| a7bc716b95 | |||
| 9b36224bd4 | |||
| 9f1788f202 | |||
| a30040411c | |||
| c4d4065dc2 | |||
| d4be12ab21 | |||
| 02de48e1ba | |||
| 97d6db966c | |||
| cf2fc58c65 | |||
| e1b1ce684d | |||
| 793f20d728 | |||
| 15ebb4b8ad | |||
| 80a294b334 | |||
| 6636c14c2a | |||
| bf898f90bc | |||
| de7a3861f3 | |||
| d4951c68f3 | |||
| 85e90c33fc | |||
| 22632a227c | |||
| fc8004d335 | |||
| 2149670328 | |||
| 2741eb1fe3 | |||
| b09614b61b | |||
| 2257ae987f | |||
| 88949d7f94 | |||
| e34d00a958 | |||
| 6b15731bfe | |||
| be30728bc2 | |||
| d34e1b524a | |||
| 3debb33346 | |||
| 79635ccb41 | |||
| d8097b48ea | |||
| 1116455b3f | |||
| 09802ac217 | |||
| 984586c20a | |||
| a591160c32 | |||
| 6dd3215eae | |||
| e96c365054 | |||
| 2150cac956 | |||
| 096b4d8b1b | |||
| 47e3e73c8f | |||
| 519ab3a9c8 | |||
| 5df99d3e61 | |||
| bd5eec7bb7 | |||
| 2086e80e4d | |||
| f84168253b | |||
| a390bd2e73 | |||
| 456f02b476 | |||
| 027de54558 | |||
| 7dd34b1212 | |||
| 24274acd6a | |||
| 5099caa55a | |||
| 17e5c03a02 | |||
| a31b37e733 | |||
| 35e4784202 | |||
| f464620a65 | |||
| 2d639b83ab | |||
| 406747ab30 | |||
| b41c1d7655 | |||
| b10b8a470b | |||
| 23ffdcc314 | |||
| ffdbdf7496 | |||
| df636371a7 | |||
| ceff4dae61 | |||
| 540d4af8a7 | |||
| bb995c53c5 | |||
| eacb4b3f3e | |||
| a70b99ff62 | |||
| 2c2fd5f3a0 | |||
| 288c40e2a6 | |||
| d85d3a4493 | |||
| a1c0c5e1a2 | |||
| 6ccc8173d3 | |||
| 2cb342c3b8 | |||
| 58db31e61e | |||
| 30cca2524f | |||
| 53ad7ebfea | |||
| bff2c20fce | |||
| e4104e63d9 | |||
| bc5145e267 | |||
| 544495563f | |||
| 112a277998 | |||
| 46657ad8a6 | |||
| 14b9eba428 | |||
| 6a31fecca3 | |||
| 3ccbc25dba | |||
| c3c255b0ca | |||
| 2e42b0555a | |||
| c1c6ce22e8 | |||
| a3bc3bbf2d | |||
| 37e19534f1 | |||
| d290f44fc7 | |||
| 4966d632ae | |||
| a5871c1811 | |||
| 0f40acb9a8 | |||
| cf2af3ce4d | |||
| d54f314c93 | |||
| ff078db8cb | |||
| acc35849af | |||
| c9c91c7e15 | |||
| ce5f8d72a4 | |||
| b4c10eea60 | |||
| c358373dfa | |||
| 95ddadd146 | |||
| 91392c5a87 | |||
| 26b70bce28 | |||
| 7f81b14eff | |||
| 87d385303f | |||
| eaf96335ad | |||
| d7163c9b5a | |||
| 98ff74a70e | |||
| f1b948d4dc | |||
| 9b6b02af6d | |||
| 6b45835ecf | |||
| c02c815603 | |||
| 5bf9aab464 | |||
| 722b81627c | |||
| 7e2659f017 | |||
| 52b3187962 | |||
| b3c5aaa1ca | |||
| ea62d23cb3 | |||
| 289f64b191 | |||
| 5878d27068 | |||
| 071e371803 | |||
| ccf2bf74ea | |||
| 9e439305fd | |||
| dadd67e7bd | |||
| 762b0d9510 | |||
| dc0a7428bd | |||
| 7534fae9bc | |||
| 68a9ceacbb | |||
| 8a3a5a2ff1 | |||
| 8995c81103 | |||
| 095f9114dd | |||
| 470a0ad611 | |||
| 9661001af1 | |||
| 17f869b2fb | |||
| 162ba722f6 | |||
| 4756d18a14 | |||
| 9cff832282 | |||
| 7ced22ccc6 | |||
| 6455722f29 | |||
| 8b3d99308c | |||
| f87319f834 | |||
| 4605c2cc2d | |||
| 49e567a5cc | |||
| 7580518bd8 | |||
| d93b5af265 | |||
| 9a0c78b831 | |||
| 61a37331ce | |||
| d265d290c3 | |||
| aff7594ef3 | |||
| 5e586995a8 | |||
| 1d0e45e179 | |||
| cab94eb719 | |||
| c1cc4df26e | |||
| b4f154f9b4 | |||
| ac3f1d6f61 | |||
| d27f2929d2 | |||
| 6aee04bba4 | |||
| a88b253c78 | |||
| 751c1b0418 | |||
| a0feffb2a7 | |||
| 34d17e6168 | |||
| c30377392b | |||
| 0d2482a1a9 | |||
| 149814d95b | |||
| 16e24dc4c5 | |||
| dc11b87a1d | |||
| c8d34eaf9c | |||
| 9183ade655 | |||
| 678afc4906 | |||
| 6d3b5b24fd | |||
| 4286eeb531 | |||
| 0187537a2e | |||
| 928947f1c3 | |||
| fb1ebf13c6 | |||
| ed7e9be7cd | |||
| e903c8b57b | |||
| 210832ddf1 | |||
| 96ebefc597 | |||
| 5348e2eb54 | |||
| f6e6f465f2 | |||
| ce87f933a4 | |||
| a04af7854d | |||
| 75c7fd2886 | |||
| 92b4d52079 | |||
| 91b4045791 | |||
| 0f87a0248f | |||
| 3c9421381b | |||
| 2d946d2766 | |||
| a422f19fac | |||
| ebb1a70abc | |||
| b9d013e3ce | |||
| b044ec0420 | |||
| 3520529430 | |||
| 864f0342af | |||
| 5a2e99c975 | |||
| d07f459409 | |||
| 3d5d6b5ad1 | |||
| 80dcec773a | |||
| 43738dacfb | |||
| 78b10de7d6 | |||
| 37db8a13d4 | |||
| eb8b2210cd | |||
| 118033cac6 | |||
| b0ba69ff31 | |||
| c1241b1691 | |||
| ad813da7b1 | |||
| b4f04c18db | |||
| 23985428ea | |||
| 018fed6014 | |||
| 3be50224ff | |||
| 0213928735 | |||
| 1cf599d494 | |||
| 8a4dbdedcf | |||
| 35b316f93f | |||
| 7e451a24bc | |||
| f8620704d4 | |||
| c6687e159b | |||
| 8227bc41cd | |||
| 6f5caaa87a | |||
| e5dac9fa24 | |||
| 48bb8aeb01 | |||
| fd32501fe0 | |||
| 0a475bdab3 | |||
| 3d6ab2b53b | |||
| 5e315495c2 | |||
| b353637e45 | |||
| fb6290c87a | |||
| 76cb5a50c0 | |||
| 5c40ef090a | |||
| 9c9b161f51 | |||
| 9b257ad862 | |||
| 5edc4c95bc | |||
| f9bd4c5d24 | |||
| 88a5f7d994 | |||
| 04b4c7c042 | |||
| 87d06bae85 | |||
| c096c6edc3 | |||
| 713c5c4275 | |||
| e48c7675b9 | |||
| f824986742 | |||
| 5e788ae1fa | |||
| 78b390da31 | |||
| b3257a315d | |||
| fc1763dc6b | |||
| 7b948b0580 | |||
| 379ddd88d6 | |||
| 23c015ba52 | |||
| 0f97d9555c | |||
| 0992a77cfb | |||
| 4b1f93b61c | |||
| 916122b166 | |||
| a3b351738a | |||
| 37f8607a88 | |||
| a4895e4df1 | |||
| a9a92892be | |||
| d8770d1284 | |||
| e3d8758a32 | |||
| 5a5496f4e7 | |||
| b33cfce2fa | |||
| 025ab0dabb | |||
| 63b078b550 | |||
| fa1288c6b3 | |||
| 9f9466d001 | |||
| 249df3864b | |||
| 615f0c4a1c | |||
| a096f19bb2 | |||
| a967acee4c | |||
| 2801edc451 | |||
| acebc3f41e | |||
| 9404389d83 | |||
| 60b75b25f8 | |||
| 608fe55889 | |||
| 37d2c0a4be | |||
| 3ccbc984f4 | |||
| 8bd3ce2fe2 | |||
| ebcbf85396 | |||
| 44a6d2a98c | |||
| 8ee368ddad | |||
| 34069e00c4 | |||
| 88542819f0 | |||
| 8a66c30895 | |||
| e6579a3c0a | |||
| 9016093b4c | |||
| 301e4988a1 | |||
| e6391d84a1 | |||
| 4c305ef459 | |||
| 72e308788f | |||
| f1dc6e1441 | |||
| 33801c311e | |||
| 0cd68cfedc | |||
| e00f483238 | |||
| 5cc8df3723 | |||
| 08bf128255 | |||
| 3979508dae | |||
| a5a2fe9180 | |||
| 29d63893ff | |||
| 39354eaaf1 | |||
| 2e8418b362 | |||
| 1a93891222 | |||
| 099ccfa254 | |||
| d41b2c5401 | |||
| 5f7bc2cf4d | |||
| 6d330e8a3f | |||
| 72b680ca2d | |||
| 5e1c3732e5 | |||
| 7251c65996 | |||
| fea0ba84c5 | |||
| cdfc6ff5d5 | |||
| 50721da77b | |||
| 14cbec6d6b | |||
| ea334cfa18 | |||
| c10af30381 | |||
| 4351609df9 | |||
| c430fe6612 | |||
| 3501007074 | |||
| 603828366f | |||
| e46df97c3a | |||
| 180bb9fa9f | |||
| 81f1c04b85 | |||
| b722ce6cc3 | |||
| 65fa896bc2 | |||
| 464b31157e | |||
| a9f7764879 | |||
| 047ae7fb23 | |||
| 98db90ba70 | |||
| 0b11762b1c | |||
| 127ab3cb47 | |||
| 09cc0c5cc9 | |||
| c4c5e34110 | |||
| 89d60a2680 | |||
| 369ec65d12 | |||
| 40c2f47cd9 | |||
| bc22799160 | |||
| 4c1510b729 | |||
| d44b98d3dd | |||
| a4e842f19a | |||
| aec0f06501 | |||
| f9e4d0a5e2 | |||
| 3093c63024 | |||
| b0edbd8ebd | |||
| 5dd20db05f | |||
| 0c140d5f6e | |||
| 49969825ad | |||
| 3b21499b34 | |||
| 3fde29ead4 | |||
| afe35ca12d | |||
| a83e37c799 | |||
| 3c7541e97c | |||
| 3ec3ebb3db | |||
| 3e71827bea | |||
| 6adac6a673 | |||
| 88dc71381f | |||
| c6ca9be406 | |||
| 7dddf59942 | |||
| 8ef32821be | |||
| 1e9f0409c3 | |||
| bcec9cea78 | |||
| 87cfbb3a2b | |||
| 3b1396b538 | |||
| ce1a1996f7 | |||
| 5f2e8f7723 | |||
| 54cd174f61 | |||
| 28be8496a9 | |||
| 3ae7bfc298 | |||
| 1cbe1896e7 | |||
| ebe3872c64 | |||
| f1ea0bcafc | |||
| cf8fc2294e | |||
| 1636c6dbd8 | |||
| 59ff8388ea | |||
| 1c58d8b226 | |||
| 0f58bd02c1 | |||
| 4b4901519d | |||
| a711d4e39a | |||
| 52dc90d5fd | |||
| 9a1cbabdd6 | |||
| 596ab7552e | |||
| ea23a09c05 | |||
| 394bd17251 | |||
| 627612c9e0 | |||
| c6fa2dd8e2 | |||
| 8d38228c98 | |||
| bbba8fd09d | |||
| 5e928fc988 | |||
| 7e36763631 | |||
| 542cf79595 | |||
| 2ea3765359 | |||
| b7aaebf94f | |||
| c9cb258616 | |||
| 697ad1ca9e | |||
| 6a838718df | |||
| 6a21922854 | |||
| c81a05cbb1 | |||
| 956a4419d8 | |||
| f356ef90d7 | |||
| a8fd55d90d | |||
| 208037b56a | |||
| 62887c67ab | |||
| 9960596f4e | |||
| b5ef21b22d | |||
| 44fb817a4e | |||
| 2c54dcda89 | |||
| 0bb7202ff7 | |||
| 75eaea1dff | |||
| 93b6c83814 | |||
| d9df271113 | |||
| 13461698e4 | |||
| eaa5dd0282 | |||
| 672cbc5378 | |||
| 2f568c9766 | |||
| c4c972fd2f | |||
| 6de259e0ac | |||
| 1f93b4e842 | |||
| 4c2d2f4dc9 | |||
| 148c16bf70 | |||
| 77af1a7127 | |||
| 2f0ec8256f | |||
| 913df0c5ed | |||
| c88109a496 | |||
| 8f5dfd0920 | |||
| ae58a385b7 | |||
| 3160b23291 | |||
| 73a0b690e9 | |||
| bdb4890d04 | |||
| 9091dd7833 | |||
| a8a35f4d07 | |||
| 0d24b22485 | |||
| e8a696cff9 | |||
| 811ef19ada | |||
| db696a2dda | |||
| 3d81009347 | |||
| 0b665700b5 | |||
| 1e407482be | |||
| d343c66f85 | |||
| 0a0d89296b | |||
| fede36d515 | |||
| 527a16852e | |||
| aab7c187f2 | |||
| dd7b074dfa | |||
| 71e93c2b79 | |||
| ca161e2a96 | |||
| ab8691d84b | |||
| a48cf28e17 | |||
| cd7c974fb6 | |||
| 3b5d032dd5 | |||
| f8088ecd29 | |||
| e83ff69465 | |||
| 66af605697 | |||
| 71dc81d420 | |||
| 02c9bb76ff | |||
| 7b388eb4a8 | |||
| c770089b50 | |||
| afaac05f61 | |||
| aba8199a82 | |||
| 0134b1ebfa | |||
| 339e073399 | |||
| 4feafda822 | |||
| 5ac339b866 | |||
| 95c90ddbc4 | |||
| 2417c8a49e | |||
| 26d0adf2fe | |||
| 734fdbddea | |||
| 33144941a9 | |||
| f309920bef | |||
| e78a309111 | |||
| 46a4ad4f3c | |||
| 3842c0d766 | |||
| 8369c1f05b | |||
| cadf1c232e | |||
| 713651738c | |||
| 1475245465 | |||
| 281bbf4bd4 | |||
| a2bbbb1448 | |||
| 87a28957aa | |||
| 01de6c565a | |||
| f3e341edac | |||
| c5d6af3c5a | |||
| cc1577054d | |||
| 3e6b18b2d0 | |||
| 7eb505e6dd | |||
| 91ca1cf985 | |||
| f8a4bcdd50 | |||
| 418d401bf9 | |||
| d0afd774b7 | |||
| 0af65cae37 | |||
| 4d50fe5f12 | |||
| f24aca61a9 | |||
| d3e2226b38 | |||
| 73fa6259be | |||
| dffc2a8b1c | |||
| fd634fdec8 | |||
| 328ebaefde | |||
| 4de8c4e5db | |||
| 8dc3cb2ffb | |||
| c67ece0f6b | |||
| 6640e120b7 | |||
| 6f7b394d99 | |||
| 91f47645b0 | |||
| 18903cc8f7 | |||
| 7e467484a8 | |||
| 21bf6abd41 | |||
| 401728b2a2 | |||
| bfd0dc3314 | |||
| ad92b12559 | |||
| 4391992b8c | |||
| 728e6d735a | |||
| 66df8c3bb3 | |||
| ca08d21da0 | |||
| 7d0f458f11 | |||
| f15aeb3bbc | |||
| 9333244925 | |||
| 50f5ab8515 | |||
| 4df0d2e7e8 | |||
| f2e17a0b68 | |||
| e372971f2b | |||
| 3bf30377b8 | |||
| 67b67b6d66 | |||
| b8390f15d4 | |||
| 5ac3f9bfa5 | |||
| 02fe013d2f | |||
| 5e5e4fe3a4 | |||
| d49e4e53fd | |||
| 2d11290121 | |||
| 1a197292da | |||
| 3b59979746 | |||
| 259d619af3 |
@@ -26,7 +26,7 @@ RewriteRule ^.well-known/carddav /remote.php/carddav/ [R]
|
||||
RewriteRule ^.well-known/caldav /remote.php/caldav/ [R]
|
||||
RewriteRule ^apps/calendar/caldav.php remote.php/caldav/ [QSA,L]
|
||||
RewriteRule ^apps/contacts/carddav.php remote.php/carddav/ [QSA,L]
|
||||
RewriteRule ^apps/([^/]*)/(.*\.(css|php))$ index.php?app=$1&getfile=$2 [QSA,L]
|
||||
RewriteRule ^apps/([^/]*)/(.*\.(php))$ index.php?app=$1&getfile=$2 [QSA,L]
|
||||
RewriteRule ^remote/(.*) remote.php [QSA,L]
|
||||
</IfModule>
|
||||
<IfModule mod_mime.c>
|
||||
@@ -38,3 +38,6 @@ DirectoryIndex index.php index.html
|
||||
</IfModule>
|
||||
AddDefaultCharset utf-8
|
||||
Options -Indexes
|
||||
<IfModule pagespeed_module>
|
||||
ModPagespeed Off
|
||||
</IfModule>
|
||||
|
||||
+1
-1
Submodule 3rdparty updated: 42efd96628...331d7f0e8f
@@ -18,7 +18,7 @@ if(\OC\Files\Filesystem::file_exists($target . '/' . $file)) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($dir != '' || $file != 'Shared') {
|
||||
if ($target != '' || strtolower($file) != 'shared') {
|
||||
$targetFile = \OC\Files\Filesystem::normalizePath($target . '/' . $file);
|
||||
$sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file);
|
||||
if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) {
|
||||
|
||||
@@ -21,6 +21,7 @@ if (empty($_POST['dirToken'])) {
|
||||
} else {
|
||||
// return only read permissions for public upload
|
||||
$allowedPermissions = OCP\PERMISSION_READ;
|
||||
$public_directory = !empty($_POST['subdir']) ? $_POST['subdir'] : '/';
|
||||
|
||||
$linkItem = OCP\Share::getShareByToken($_POST['dirToken']);
|
||||
if ($linkItem === false) {
|
||||
@@ -34,6 +35,7 @@ if (empty($_POST['dirToken'])) {
|
||||
// resolve reshares
|
||||
$rootLinkItem = OCP\Share::resolveReShare($linkItem);
|
||||
|
||||
OCP\JSON::checkUserExists($rootLinkItem['uid_owner']);
|
||||
// Setup FS with owner
|
||||
OC_Util::tearDownFS();
|
||||
OC_Util::setupFS($rootLinkItem['uid_owner']);
|
||||
@@ -43,7 +45,7 @@ if (empty($_POST['dirToken'])) {
|
||||
$dir = sprintf(
|
||||
"/%s/%s",
|
||||
$path,
|
||||
isset($_POST['subdir']) ? $_POST['subdir'] : ''
|
||||
$public_directory
|
||||
);
|
||||
|
||||
if (!$dir || empty($dir) || $dir === false) {
|
||||
@@ -77,7 +79,9 @@ foreach ($_FILES['files']['error'] as $error) {
|
||||
UPLOAD_ERR_NO_TMP_DIR => $l->t('Missing a temporary folder'),
|
||||
UPLOAD_ERR_CANT_WRITE => $l->t('Failed to write to disk'),
|
||||
);
|
||||
OCP\JSON::error(array('data' => array_merge(array('message' => $errors[$error]), $storageStats)));
|
||||
$errorMessage = $errors[$error];
|
||||
\OCP\Util::writeLog('files', "Upload error: $error - $errorMessage", \OCP\Util::ERROR);
|
||||
OCP\JSON::error(array('data' => array_merge(array('message' => $errorMessage), $storageStats)));
|
||||
exit();
|
||||
}
|
||||
}
|
||||
@@ -110,7 +114,14 @@ if (strpos($dir, '..') === false) {
|
||||
} else {
|
||||
$target = \OC\Files\Filesystem::normalizePath(stripslashes($dir).'/'.$files['name'][$i]);
|
||||
}
|
||||
|
||||
|
||||
$directory = \OC\Files\Filesystem::normalizePath(stripslashes($dir));
|
||||
if (isset($public_directory)) {
|
||||
// If we are uploading from the public app,
|
||||
// we want to send the relative path in the ajax request.
|
||||
$directory = $public_directory;
|
||||
}
|
||||
|
||||
if ( ! \OC\Files\Filesystem::file_exists($target)
|
||||
|| (isset($_POST['resolution']) && $_POST['resolution']==='replace')
|
||||
) {
|
||||
@@ -136,7 +147,8 @@ if (strpos($dir, '..') === false) {
|
||||
'originalname' => $files['tmp_name'][$i],
|
||||
'uploadMaxFilesize' => $maxUploadFileSize,
|
||||
'maxHumanFilesize' => $maxHumanFileSize,
|
||||
'permissions' => $meta['permissions'] & $allowedPermissions
|
||||
'permissions' => $meta['permissions'] & $allowedPermissions,
|
||||
'directory' => $directory,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -163,7 +175,8 @@ if (strpos($dir, '..') === false) {
|
||||
'originalname' => $files['tmp_name'][$i],
|
||||
'uploadMaxFilesize' => $maxUploadFileSize,
|
||||
'maxHumanFilesize' => $maxHumanFileSize,
|
||||
'permissions' => $meta['permissions'] & $allowedPermissions
|
||||
'permissions' => $meta['permissions'] & $allowedPermissions,
|
||||
'directory' => $directory,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ $server->addPlugin(new OC_Connector_Sabre_FilesPlugin());
|
||||
$server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin());
|
||||
$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin());
|
||||
$server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin());
|
||||
$server->addPlugin(new OC_Connector_Sabre_ExceptionLoggerPlugin('webdav'));
|
||||
|
||||
// And off we go!
|
||||
$server->exec();
|
||||
|
||||
@@ -3,17 +3,14 @@
|
||||
// fix webdav properties,add namespace in front of the property, update for OC4.5
|
||||
$installedVersion=OCP\Config::getAppValue('files', 'installed_version');
|
||||
if (version_compare($installedVersion, '1.1.6', '<')) {
|
||||
$query = OC_DB::prepare( 'SELECT `propertyname`, `propertypath`, `userid` FROM `*PREFIX*properties`' );
|
||||
$result = $query->execute();
|
||||
$updateQuery = OC_DB::prepare('UPDATE `*PREFIX*properties`'
|
||||
.' SET `propertyname` = ?'
|
||||
.' WHERE `userid` = ?'
|
||||
.' AND `propertypath` = ?');
|
||||
while( $row = $result->fetchRow()) {
|
||||
if ( $row['propertyname'][0] != '{' ) {
|
||||
$updateQuery->execute(array('{DAV:}' + $row['propertyname'], $row['userid'], $row['propertypath']));
|
||||
}
|
||||
}
|
||||
$concat = OC_DB::getConnection()->getDatabasePlatform()->
|
||||
getConcatExpression( '\'{DAV:}\'', '`propertyname`' );
|
||||
$query = OC_DB::prepare('
|
||||
UPDATE `*PREFIX*properties`
|
||||
SET `propertyname` = ' . $concat . '
|
||||
WHERE `propertyname` NOT LIKE \'{%\'
|
||||
');
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
//update from OC 3
|
||||
|
||||
@@ -57,6 +57,7 @@ class Scan extends Command {
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
\OC_App::loadApps('authentication');
|
||||
if ($input->getOption('all')) {
|
||||
$users = $this->userManager->search('');
|
||||
} else {
|
||||
|
||||
@@ -65,9 +65,14 @@
|
||||
top: 44px;
|
||||
width: 100%;
|
||||
}
|
||||
#filestable, #controls {
|
||||
min-width: 680px;
|
||||
/* make sure there's enough room for the file actions */
|
||||
#body-user #filestable {
|
||||
min-width: 750px;
|
||||
}
|
||||
#body-user #controls {
|
||||
min-width: 600px;
|
||||
}
|
||||
|
||||
#filestable tbody tr { background-color:#fff; height:2.5em; }
|
||||
#filestable tbody tr:hover, tbody tr:active {
|
||||
background-color: rgb(240,240,240);
|
||||
@@ -98,7 +103,7 @@ table td {
|
||||
}
|
||||
table th#headerName {
|
||||
position: relative;
|
||||
width: 100em; /* not really sure why this works better than 100% … table styling */
|
||||
width: 9999px; /* not really sure why this works better than 100% … table styling */
|
||||
padding: 0;
|
||||
}
|
||||
#headerName-container {
|
||||
@@ -140,7 +145,7 @@ table.multiselect thead th {
|
||||
}
|
||||
table.multiselect #headerName {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
width: 9999px; /* when we use 100%, the styling breaks on mobile … table styling */
|
||||
}
|
||||
table td.selection, table th.selection, table td.fileaction { width:2em; text-align:center; }
|
||||
table td.filename a.name {
|
||||
@@ -237,7 +242,7 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; }
|
||||
|
||||
#fileList tr td.filename a.name label {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
width: 80%;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
margin: -5px -3px;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
background-image: url('%webroot%/core/img/actions/upload.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
opacity: .65;
|
||||
}
|
||||
.file_upload_target { display:none; }
|
||||
@@ -119,11 +116,6 @@
|
||||
.oc-dialog .fileexists .conflict input[type='checkbox'] {
|
||||
float: left;
|
||||
}
|
||||
.oc-dialog .fileexists .toggle {
|
||||
background-image: url('%webroot%/core/img/actions/triangle-e.png');
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.oc-dialog .fileexists #allfileslabel {
|
||||
float:right;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
// Check if we are a user
|
||||
OCP\User::checkLoggedIn();
|
||||
|
||||
// don't block php session during download
|
||||
session_write_close();
|
||||
|
||||
$filename = $_GET["file"];
|
||||
|
||||
if(!\OC\Files\Filesystem::file_exists($filename)) {
|
||||
@@ -37,12 +40,7 @@ if(!\OC\Files\Filesystem::file_exists($filename)) {
|
||||
$ftype=\OC\Files\Filesystem::getMimeType( $filename );
|
||||
|
||||
header('Content-Type:'.$ftype);
|
||||
if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) {
|
||||
header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' );
|
||||
} else {
|
||||
header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode( basename($filename) )
|
||||
. '; filename="' . rawurlencode( basename($filename) ) . '"' );
|
||||
}
|
||||
OCP\Response::setContentDispositionHeader(basename($filename), 'attachment');
|
||||
OCP\Response::disableCaching();
|
||||
header('Content-Length: '.\OC\Files\Filesystem::filesize($filename));
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ OC.Upload = {
|
||||
*/
|
||||
isProcessing:function() {
|
||||
var count = 0;
|
||||
|
||||
|
||||
jQuery.each(this._uploads,function(i, data) {
|
||||
if (data.state() === 'pending') {
|
||||
count++;
|
||||
@@ -170,7 +170,7 @@ OC.Upload = {
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
if ( $('#file_upload_start').exists() ) {
|
||||
if ( $('#file_upload_start').exists()&& $('#file_upload_start').is(':visible') ) {
|
||||
|
||||
var file_upload_param = {
|
||||
dropZone: $('#content'), // restrict dropZone to content div
|
||||
@@ -196,13 +196,13 @@ $(document).ready(function() {
|
||||
add: function(e, data) {
|
||||
OC.Upload.log('add', e, data);
|
||||
var that = $(this);
|
||||
|
||||
|
||||
// we need to collect all data upload objects before starting the upload so we can check their existence
|
||||
// and set individual conflict actions. unfortunately there is only one variable that we can use to identify
|
||||
// the selection a data upload is part of, so we have to collect them in data.originalFiles
|
||||
// turning singleFileUploads off is not an option because we want to gracefully handle server errors like
|
||||
// already exists
|
||||
|
||||
|
||||
// create a container where we can store the data objects
|
||||
if ( ! data.originalFiles.selection ) {
|
||||
// initialize selection and remember number of files to upload
|
||||
@@ -213,32 +213,40 @@ $(document).ready(function() {
|
||||
};
|
||||
}
|
||||
var selection = data.originalFiles.selection;
|
||||
|
||||
|
||||
// add uploads
|
||||
if ( selection.uploads.length < selection.filesToUpload ) {
|
||||
// remember upload
|
||||
selection.uploads.push(data);
|
||||
}
|
||||
|
||||
|
||||
//examine file
|
||||
var file = data.files[0];
|
||||
|
||||
try {
|
||||
// FIXME: not so elegant... need to refactor that method to return a value
|
||||
Files.isFileNameValid(file.name, FileList.getCurrentDirectory());
|
||||
}
|
||||
catch (errorMessage) {
|
||||
data.textStatus = 'invalidcharacters';
|
||||
data.errorThrown = errorMessage;
|
||||
}
|
||||
|
||||
if (file.type === '' && file.size === 4096) {
|
||||
data.textStatus = 'dirorzero';
|
||||
data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes',
|
||||
{filename: file.name}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// add size
|
||||
selection.totalBytes += file.size;
|
||||
|
||||
|
||||
//check max upload size
|
||||
if (selection.totalBytes > $('#max_upload').val()) {
|
||||
data.textStatus = 'notenoughspace';
|
||||
data.errorThrown = t('files', 'Not enough space available');
|
||||
}
|
||||
|
||||
|
||||
// end upload for whole selection on error
|
||||
if (data.errorThrown) {
|
||||
// trigger fileupload fail
|
||||
@@ -249,12 +257,12 @@ $(document).ready(function() {
|
||||
|
||||
// check existing files when all is collected
|
||||
if ( selection.uploads.length >= selection.filesToUpload ) {
|
||||
|
||||
|
||||
//remove our selection hack:
|
||||
delete data.originalFiles.selection;
|
||||
|
||||
var callbacks = {
|
||||
|
||||
|
||||
onNoConflicts: function (selection) {
|
||||
$.each(selection.uploads, function(i, upload) {
|
||||
upload.submit();
|
||||
@@ -277,7 +285,7 @@ $(document).ready(function() {
|
||||
};
|
||||
|
||||
OC.Upload.checkExistingFiles(selection, callbacks);
|
||||
|
||||
|
||||
}
|
||||
|
||||
return true; // continue adding files
|
||||
@@ -400,7 +408,7 @@ $(document).ready(function() {
|
||||
});
|
||||
fileupload.on('fileuploadstop', function(e, data) {
|
||||
OC.Upload.log('progress handle fileuploadstop', e, data);
|
||||
|
||||
|
||||
$('#uploadprogresswrapper input.stop').fadeOut();
|
||||
$('#uploadprogressbar').fadeOut();
|
||||
Files.updateStorageStatistics();
|
||||
@@ -492,7 +500,7 @@ $(document).ready(function() {
|
||||
if ($(this).children('p').length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$('#new .error').tipsy('hide');
|
||||
|
||||
$('#new li').each(function(i,element) {
|
||||
@@ -506,7 +514,7 @@ $(document).ready(function() {
|
||||
var text=$(this).children('p').text();
|
||||
$(this).data('text',text);
|
||||
$(this).children('p').remove();
|
||||
|
||||
|
||||
// add input field
|
||||
var form = $('<form></form>');
|
||||
var input = $('<input type="text">');
|
||||
@@ -523,7 +531,7 @@ $(document).ready(function() {
|
||||
throw t('files', 'URL cannot be empty');
|
||||
} else if (type !== 'web' && !Files.isFileNameValid(filename)) {
|
||||
// Files.isFileNameValid(filename) throws an exception itself
|
||||
} else if ($('#dir').val() === '/' && filename === 'Shared') {
|
||||
} else if (FileList.getCurrentDirectory() === '/' && filename.toLowerCase() === 'shared') {
|
||||
throw t('files', 'In the home folder \'Shared\' is a reserved filename');
|
||||
} else if (FileList.inList(filename)) {
|
||||
throw t('files', '{new_name} already exists', {new_name: filename});
|
||||
@@ -605,7 +613,7 @@ $(document).ready(function() {
|
||||
if (result.status === 'success') {
|
||||
var date=new Date();
|
||||
FileList.addDir(name, 0, date, hidden);
|
||||
var tr=$('tr[data-file="'+name+'"]');
|
||||
var tr = FileList.findFileEl(name);
|
||||
tr.attr('data-id', result.data.id);
|
||||
} else {
|
||||
OC.dialogs.alert(result.data.message, t('core', 'Could not create folder'));
|
||||
@@ -647,7 +655,7 @@ $(document).ready(function() {
|
||||
$('#uploadprogressbar').fadeOut();
|
||||
var date = new Date();
|
||||
FileList.addFile(localName, size, date, false, hidden);
|
||||
var tr = $('tr[data-file="'+localName+'"]');
|
||||
var tr = FileList.findFileEl(localName);
|
||||
tr.data('mime', mime).data('id', id);
|
||||
tr.attr('data-id', id);
|
||||
var path = $('#dir').val()+'/'+localName;
|
||||
|
||||
@@ -71,7 +71,7 @@ var FileActions = {
|
||||
FileActions.currentFile = parent;
|
||||
var actions = FileActions.get(FileActions.getCurrentMimeType(), FileActions.getCurrentType(), FileActions.getCurrentPermissions());
|
||||
var file = FileActions.getCurrentFile();
|
||||
if ($('tr[data-file="'+file+'"]').data('renaming')) {
|
||||
if (FileList.findFileEl(file).data('renaming')) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -103,9 +103,9 @@ var FileActions = {
|
||||
}
|
||||
var html = '<a href="#" class="action" data-action="' + name + '">';
|
||||
if (img) {
|
||||
html += '<img class ="svg" src="' + img + '" /> ';
|
||||
html += '<img class ="svg" src="' + img + '" />';
|
||||
}
|
||||
html += t('files', name) + '</a>';
|
||||
html += '<span> ' + t('files', name) + '</span></a>';
|
||||
|
||||
var element = $(html);
|
||||
element.data('action', name);
|
||||
@@ -163,13 +163,18 @@ var FileActions = {
|
||||
};
|
||||
|
||||
$(document).ready(function () {
|
||||
var hasDownloadAction = false;
|
||||
var downloadScope;
|
||||
if ($('#allowZipDownload').val() == 1) {
|
||||
var downloadScope = 'all';
|
||||
downloadScope = 'all';
|
||||
} else {
|
||||
var downloadScope = 'file';
|
||||
downloadScope = 'file';
|
||||
}
|
||||
|
||||
if (typeof disableDownloadActions == 'undefined' || !disableDownloadActions) {
|
||||
hasDownloadAction = FileActions.actions[downloadScope] && FileActions.actions[downloadScope].Download;
|
||||
|
||||
// do not override download action if it exists (might have been registered by an app already)
|
||||
if ((typeof disableDownloadActions == 'undefined' || !disableDownloadActions) && !hasDownloadAction) {
|
||||
FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () {
|
||||
return OC.imagePath('core', 'actions/download');
|
||||
}, function (filename) {
|
||||
|
||||
+40
-27
@@ -6,6 +6,13 @@ var FileList={
|
||||
$(this).attr('data-file',decodeURIComponent($(this).attr('data-file')));
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Returns the tr element for a given file name
|
||||
*/
|
||||
findFileEl: function(fileName){
|
||||
// use filterAttr to avoid escaping issues
|
||||
return $('#fileList tr').filterAttr('data-file', fileName);
|
||||
},
|
||||
update:function(fileListHtml) {
|
||||
var $fileList = $('#fileList');
|
||||
$fileList.empty().html(fileListHtml);
|
||||
@@ -20,6 +27,8 @@ var FileList={
|
||||
Files.setupDragAndDrop();
|
||||
}
|
||||
FileList.updateFileSummary();
|
||||
procesSelection();
|
||||
|
||||
$fileList.trigger(jQuery.Event("updated"));
|
||||
},
|
||||
createRow:function(type, name, iconurl, linktarget, size, lastModified, permissions) {
|
||||
@@ -292,8 +301,12 @@ var FileList={
|
||||
$('#filestable').toggleClass('hidden', show);
|
||||
},
|
||||
remove:function(name){
|
||||
$('tr').filterAttr('data-file',name).find('td.filename').draggable('destroy');
|
||||
$('tr').filterAttr('data-file',name).remove();
|
||||
var fileEl = FileList.findFileEl(name);
|
||||
if (fileEl.data('permissions') & OC.PERMISSION_DELETE) {
|
||||
// file is only draggable when delete permissions are set
|
||||
fileEl.find('td.filename').draggable('destroy');
|
||||
}
|
||||
fileEl.remove();
|
||||
FileList.updateFileSummary();
|
||||
if ( ! $('tr[data-file]').exists() ) {
|
||||
$('#emptycontent').removeClass('hidden');
|
||||
@@ -334,7 +347,7 @@ var FileList={
|
||||
FileList.updateFileSummary();
|
||||
},
|
||||
loadingDone:function(name, id) {
|
||||
var mime, tr = $('tr[data-file="'+name+'"]');
|
||||
var mime, tr = FileList.findFileEl(name);
|
||||
tr.data('loading', false);
|
||||
mime = tr.data('mime');
|
||||
tr.attr('data-mime', mime);
|
||||
@@ -347,12 +360,12 @@ var FileList={
|
||||
}, null, null, tr.attr('data-etag'));
|
||||
tr.find('td.filename').draggable(dragOptions);
|
||||
},
|
||||
isLoading:function(name) {
|
||||
return $('tr[data-file="'+name+'"]').data('loading');
|
||||
isLoading:function(file) {
|
||||
return FileList.findFileEl(file).data('loading');
|
||||
},
|
||||
rename:function(oldname) {
|
||||
var tr, td, input, form;
|
||||
tr = $('tr[data-file="'+oldname+'"]');
|
||||
tr = FileList.findFileEl(oldname);
|
||||
tr.data('renaming',true);
|
||||
td = tr.children('td.filename');
|
||||
input = $('<input type="text" class="filename"/>').val(oldname);
|
||||
@@ -367,15 +380,12 @@ var FileList={
|
||||
len = input.val().length;
|
||||
}
|
||||
input.selectRange(0, len);
|
||||
|
||||
var checkInput = function () {
|
||||
var filename = input.val();
|
||||
if (filename !== oldname) {
|
||||
if (!Files.isFileNameValid(filename)) {
|
||||
// Files.isFileNameValid(filename) throws an exception itself
|
||||
} else if($('#dir').val() === '/' && filename === 'Shared') {
|
||||
throw t('files','In the home folder \'Shared\' is a reserved filename');
|
||||
} else if (FileList.inList(filename)) {
|
||||
// Files.isFileNameValid(filename) throws an exception itself
|
||||
Files.isFileNameValid(filename, FileList.getCurrentDirectory());
|
||||
if (FileList.inList(filename)) {
|
||||
throw t('files', '{new_name} already exists', {new_name: filename});
|
||||
}
|
||||
}
|
||||
@@ -500,14 +510,16 @@ var FileList={
|
||||
form.trigger('submit');
|
||||
});
|
||||
},
|
||||
inList:function(filename) {
|
||||
return $('#fileList tr[data-file="'+filename+'"]').length;
|
||||
inList:function(file) {
|
||||
return FileList.findFileEl(file).length;
|
||||
},
|
||||
replace:function(oldName, newName, isNewFile) {
|
||||
// Finish any existing actions
|
||||
$('tr[data-file="'+oldName+'"]').hide();
|
||||
$('tr[data-file="'+newName+'"]').hide();
|
||||
var tr = $('tr[data-file="'+oldName+'"]').clone();
|
||||
var oldFileEl = FileList.findFileEl(oldName);
|
||||
var newFileEl = FileList.findFileEl(newName);
|
||||
oldFileEl.hide();
|
||||
newFileEl.hide();
|
||||
var tr = oldFileEl.clone();
|
||||
tr.attr('data-replace', 'true');
|
||||
tr.attr('data-file', newName);
|
||||
var td = tr.children('td.filename');
|
||||
@@ -559,7 +571,7 @@ var FileList={
|
||||
files=[files];
|
||||
}
|
||||
for (var i=0; i<files.length; i++) {
|
||||
var deleteAction = $('tr[data-file="'+files[i]+'"]').children("td.date").children(".action.delete");
|
||||
var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
}
|
||||
// Finish any existing actions
|
||||
@@ -573,7 +585,7 @@ var FileList={
|
||||
function(result) {
|
||||
if (result.status === 'success') {
|
||||
$.each(files,function(index,file) {
|
||||
var files = $('tr[data-file="'+file+'"]');
|
||||
var files = FileList.findFileEl(file);
|
||||
files.remove();
|
||||
files.find('input[type="checkbox"]').removeAttr('checked');
|
||||
files.removeClass('selected');
|
||||
@@ -595,7 +607,7 @@ var FileList={
|
||||
OC.Notification.hide();
|
||||
}, 10000);
|
||||
$.each(files,function(index,file) {
|
||||
var deleteAction = $('tr[data-file="' + file + '"] .action.delete');
|
||||
var deleteAction = FileList.findFileEl(file).find('.action.delete');
|
||||
deleteAction.removeClass('progress-icon').addClass('delete-icon');
|
||||
});
|
||||
}
|
||||
@@ -737,7 +749,7 @@ var FileList={
|
||||
},
|
||||
scrollTo:function(file) {
|
||||
//scroll to and highlight preselected file
|
||||
var $scrolltorow = $('tr[data-file="'+file+'"]');
|
||||
var $scrolltorow = FileList.findFileEl(file);
|
||||
if ($scrolltorow.exists()) {
|
||||
$scrolltorow.addClass('searchresult');
|
||||
$(window).scrollTop($scrolltorow.position().top);
|
||||
@@ -880,8 +892,8 @@ $(document).ready(function() {
|
||||
data.context.find('td.filesize').text(humanFileSize(size));
|
||||
|
||||
} else {
|
||||
// only append new file if dragged onto current dir's crumb (last)
|
||||
if (data.context && data.context.hasClass('crumb') && !data.context.hasClass('last')) {
|
||||
// only append new file if uploaded into the current folder
|
||||
if (file.directory !== FileList.getCurrentDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -949,7 +961,7 @@ $(document).ready(function() {
|
||||
$('#notification').on('click', '.undo', function() {
|
||||
if (FileList.deleteFiles) {
|
||||
$.each(FileList.deleteFiles,function(index,file) {
|
||||
$('tr[data-file="'+file+'"]').show();
|
||||
FileList.findFileEl(file).show();
|
||||
});
|
||||
FileList.deleteCanceled=true;
|
||||
FileList.deleteFiles=null;
|
||||
@@ -959,10 +971,10 @@ $(document).ready(function() {
|
||||
FileList.deleteCanceled = false;
|
||||
FileList.deleteFiles = [FileList.replaceOldName];
|
||||
} else {
|
||||
$('tr[data-file="'+FileList.replaceOldName+'"]').show();
|
||||
FileList.findFileEl(FileList.replaceOldName).show();
|
||||
}
|
||||
$('tr[data-replace="true"').remove();
|
||||
$('tr[data-file="'+FileList.replaceNewName+'"]').show();
|
||||
FileList.findFileEl(FileList.replaceNewName).show();
|
||||
FileList.replaceCanceled = true;
|
||||
FileList.replaceOldName = null;
|
||||
FileList.replaceNewName = null;
|
||||
@@ -977,7 +989,8 @@ $(document).ready(function() {
|
||||
});
|
||||
});
|
||||
$('#notification:first-child').on('click', '.suggest', function() {
|
||||
$('tr[data-file="'+$('#notification > span').attr('data-oldName')+'"]').show();
|
||||
var file = $('#notification > span').attr('data-oldName');
|
||||
FileList.findFileEl(file).show();
|
||||
OC.Notification.hide();
|
||||
});
|
||||
$('#notification:first-child').on('click', '.cancel', function() {
|
||||
|
||||
+32
-13
@@ -67,10 +67,21 @@ Files={
|
||||
return fileName;
|
||||
},
|
||||
|
||||
isFileNameValid:function (name) {
|
||||
if (name === '.') {
|
||||
throw t('files', '\'.\' is an invalid file name.');
|
||||
} else if (name.length === 0) {
|
||||
/**
|
||||
* Checks whether the given file name is valid.
|
||||
* @param name file name to check
|
||||
* @return true if the file name is valid.
|
||||
* Throws a string exception with an error message if
|
||||
* the file name is not valid
|
||||
*/
|
||||
isFileNameValid: function (name, root) {
|
||||
var trimmedName = name.trim();
|
||||
if (trimmedName === '.'
|
||||
|| trimmedName === '..'
|
||||
|| (root === '/' && trimmedName.toLowerCase() === 'shared'))
|
||||
{
|
||||
throw t('files', '"{name}" is an invalid file name.', {name: name});
|
||||
} else if (trimmedName.length === 0) {
|
||||
throw t('files', 'File name cannot be empty.');
|
||||
}
|
||||
|
||||
@@ -282,7 +293,7 @@ $(document).ready(function() {
|
||||
procesSelection();
|
||||
} else {
|
||||
var filename=$(this).parent().parent().attr('data-file');
|
||||
var tr=$('tr[data-file="'+filename+'"]');
|
||||
var tr = FileList.findFileEl(filename);
|
||||
var renaming=tr.data('renaming');
|
||||
if (!renaming && !FileList.isLoading(filename)) {
|
||||
FileActions.currentFile = $(this).parent();
|
||||
@@ -350,7 +361,12 @@ $(document).ready(function() {
|
||||
// use special download URL if provided, e.g. for public shared files
|
||||
var downloadURL = document.getElementById("downloadURL");
|
||||
if ( downloadURL ) {
|
||||
window.location = downloadURL.value+"&download&files=" + encodeURIComponent(fileslist);
|
||||
// downloading all in root of public share ? (replacement for old "Download" button)
|
||||
if ($('#isPublic').val() && dir === '/' && $('#select_all').is(':checked')) {
|
||||
window.location = downloadURL.value;
|
||||
} else {
|
||||
window.location = downloadURL.value+"&download&files=" + encodeURIComponent(fileslist);
|
||||
}
|
||||
} else {
|
||||
window.location = OC.filePath('files', 'ajax', 'download.php') + '?'+ $.param({ dir: dir, files: fileslist });
|
||||
}
|
||||
@@ -541,10 +557,12 @@ var folderDropOptions={
|
||||
if (result) {
|
||||
if (result.status === 'success') {
|
||||
//recalculate folder size
|
||||
var oldSize = $('#fileList tr[data-file="'+target+'"]').data('size');
|
||||
var newSize = oldSize + $('#fileList tr[data-file="'+file+'"]').data('size');
|
||||
$('#fileList tr[data-file="'+target+'"]').data('size', newSize);
|
||||
$('#fileList tr[data-file="'+target+'"]').find('td.filesize').text(humanFileSize(newSize));
|
||||
var oldFile = FileList.findFileEl(target);
|
||||
var newFile = FileList.findFileEl(file);
|
||||
var oldSize = oldFile.data('size');
|
||||
var newSize = oldSize + newFile.data('size');
|
||||
oldFile.data('size', newSize);
|
||||
oldFile.find('td.filesize').text(humanFileSize(newSize));
|
||||
|
||||
FileList.remove(file);
|
||||
procesSelection();
|
||||
@@ -610,11 +628,12 @@ function procesSelection() {
|
||||
return el.type==='dir';
|
||||
});
|
||||
if (selectedFiles.length === 0 && selectedFolders.length === 0) {
|
||||
$('#headerName>span.name').text(t('files','Name'));
|
||||
$('#headerName span.name').text(t('files','Name'));
|
||||
$('#headerSize').text(t('files','Size'));
|
||||
$('#modified').text(t('files','Modified'));
|
||||
$('table').removeClass('multiselect');
|
||||
$('.selectedActions').hide();
|
||||
$('#select_all').removeAttr('checked');
|
||||
}
|
||||
else {
|
||||
$('.selectedActions').show();
|
||||
@@ -717,7 +736,7 @@ Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) {
|
||||
console.warn('Files.lazyLoadPreview(): missing etag argument');
|
||||
}
|
||||
|
||||
if ( $('#publicUploadButtonMock').length ) {
|
||||
if ( $('#isPublic').length ) {
|
||||
urlSpec.t = $('#dirToken').val();
|
||||
previewURL = OC.Router.generate('core_ajax_public_preview', urlSpec);
|
||||
} else {
|
||||
@@ -738,7 +757,7 @@ Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) {
|
||||
}
|
||||
|
||||
function getUniqueName(name) {
|
||||
if ($('tr[data-file="'+name+'"]').exists()) {
|
||||
if (FileList.findFileEl(name).exists()) {
|
||||
var parts=name.split('.');
|
||||
var extension = "";
|
||||
if (parts.length > 1) {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
$TRANSLATIONS = array(
|
||||
"Save" => "Zapisz"
|
||||
);
|
||||
$PLURAL_FORMS = "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);";
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
$TRANSLATIONS = array(
|
||||
"Files" => "Файлы",
|
||||
"Share" => "Сделать общим",
|
||||
"Rename" => "Переименовать",
|
||||
"_%n folder_::_%n folders_" => array("","",""),
|
||||
"_%n file_::_%n files_" => array("","",""),
|
||||
"_Uploading %n file_::_Uploading %n files_" => array("","",""),
|
||||
"Error" => "Ошибка",
|
||||
"Size" => "Размер",
|
||||
"Upload" => "Загрузка",
|
||||
"Save" => "Сохранить",
|
||||
"Cancel upload" => "Отмена загрузки",
|
||||
"Download" => "Загрузка",
|
||||
"Delete" => "Удалить"
|
||||
);
|
||||
$PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);";
|
||||
@@ -3,8 +3,8 @@
|
||||
<span class="what">{what}<!-- If you select both versions, the copied file will have a number added to its name. --></span><br/>
|
||||
<br/>
|
||||
<table>
|
||||
<th><label><input class="allnewfiles" type="checkbox" />New Files<span class="count"></span></label></th>
|
||||
<th><label><input class="allexistingfiles" type="checkbox" />Already existing files<span class="count"></span></label></th>
|
||||
<th><label><input class="allnewfiles" type="checkbox" />{allnewfiles}<span class="count"></span></label></th>
|
||||
<th><label><input class="allexistingfiles" type="checkbox" />{allexistingfiles}<span class="count"></span></label></th>
|
||||
</table>
|
||||
<div class="conflicts">
|
||||
<div class="template">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<div id="controls">
|
||||
<?php print_unescaped($_['breadcrumb']); ?>
|
||||
<div class="actions creatable <?php if (!$_['isCreatable']):?>hidden<?php endif; ?>">
|
||||
<?php if(!isset($_['dirToken'])):?>
|
||||
<div id="new" class="button">
|
||||
<a><?php p($l->t('New'));?></a>
|
||||
<ul>
|
||||
@@ -12,21 +13,26 @@
|
||||
data-type='web'><p><?php p($l->t('From link'));?></p></li>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<div id="upload" class="button"
|
||||
title="<?php p($l->t('Upload') . ' max. '.$_['uploadMaxHumanFilesize']) ?>">
|
||||
<?php if($_['uploadMaxFilesize'] >= 0):?>
|
||||
<input type="hidden" name="MAX_FILE_SIZE" id="max_upload"
|
||||
value="<?php p($_['uploadMaxFilesize']) ?>">
|
||||
<?php endif;?>
|
||||
<?php if(isset($_['dirToken'])):?>
|
||||
<input type="hidden" id="publicUploadRequestToken" name="requesttoken" value="<?php p($_['requesttoken']) ?>" />
|
||||
<input type="hidden" id="dirToken" name="dirToken" value="<?php p($_['dirToken']) ?>" />
|
||||
<?php endif;?>
|
||||
<input type="hidden" class="max_human_file_size"
|
||||
value="(max <?php p($_['uploadMaxHumanFilesize']); ?>)">
|
||||
<input type="hidden" name="dir" value="<?php p($_['dir']) ?>" id="dir">
|
||||
<input type="file" id="file_upload_start" name='files[]'
|
||||
data-url="<?php print_unescaped(OCP\Util::linkTo('files', 'ajax/upload.php')); ?>" />
|
||||
<a href="#" class="svg"></a>
|
||||
<a href="#" class="svg icon icon-upload"></a>
|
||||
</div>
|
||||
<?php if ($_['trash']): ?>
|
||||
<input id="trash" type="button" value="<?php p($l->t('Deleted files'));?>" class="button" <?php $_['trashEmpty'] ? p('disabled') : '' ?>></input>
|
||||
<input id="trash" type="button" value="<?php p($l->t('Deleted files'));?>" class="button" <?php $_['trashEmpty'] ? p('disabled') : '' ?> />
|
||||
<?php endif; ?>
|
||||
<div id="uploadprogresswrapper">
|
||||
<div id="uploadprogressbar"></div>
|
||||
@@ -44,7 +50,7 @@
|
||||
|
||||
<div id="emptycontent" <?php if (!$_['emptyContent']):?>class="hidden"<?php endif; ?>><?php p($l->t('Nothing in here. Upload something!'))?></div>
|
||||
|
||||
<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>"></input>
|
||||
<input type="hidden" id="disableSharing" data-status="<?php p($_['disableSharing']); ?>" />
|
||||
|
||||
<table id="filestable" data-allow-public-upload="<?php p($_['publicUploadEnabled'])?>" data-preview-x="36" data-preview-y="36">
|
||||
<thead>
|
||||
@@ -69,20 +75,11 @@
|
||||
<th <?php if (!$_['fileHeader']):?>class="hidden"<?php endif; ?> id="headerDate">
|
||||
<span id="modified"><?php p($l->t( 'Modified' )); ?></span>
|
||||
<?php if ($_['permissions'] & OCP\PERMISSION_DELETE): ?>
|
||||
<!-- NOTE: Temporary fix to allow unsharing of files in root of Shared folder -->
|
||||
<?php if ($_['dir'] == '/Shared'): ?>
|
||||
<span class="selectedActions"><a href="" class="delete-selected">
|
||||
<?php p($l->t('Unshare'))?>
|
||||
<img class="svg" alt="<?php p($l->t('Unshare'))?>"
|
||||
src="<?php print_unescaped(OCP\image_path("core", "actions/delete.svg")); ?>" />
|
||||
</a></span>
|
||||
<?php else: ?>
|
||||
<span class="selectedActions"><a href="" class="delete-selected">
|
||||
<?php p($l->t('Delete'))?>
|
||||
<img class="svg" alt="<?php p($l->t('Delete'))?>"
|
||||
src="<?php print_unescaped(OCP\image_path("core", "actions/delete.svg")); ?>" />
|
||||
</a></span>
|
||||
<?php endif; ?>
|
||||
<span class="selectedActions"><a href="" class="delete-selected">
|
||||
<?php p($l->t('Delete'))?>
|
||||
<img class="svg" alt="<?php p($l->t('Delete'))?>"
|
||||
src="<?php print_unescaped(OCP\image_path("core", "actions/delete.svg")); ?>" />
|
||||
</a></span>
|
||||
<?php endif; ?>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
<?php if(count($_["breadcrumb"])):?>
|
||||
<div class="crumb" data-dir=''>
|
||||
<a href="<?php print_unescaped($_['baseURL']); ?>">
|
||||
<img src="<?php print_unescaped(OCP\image_path('core', 'places/home.svg'));?>" class="svg" />
|
||||
</a>
|
||||
</div>
|
||||
<?php endif;?>
|
||||
<div class="crumb <?php if(!count($_["breadcrumb"])) p('last');?>" data-dir=''>
|
||||
<a href="<?php print_unescaped($_['baseURL']); ?>">
|
||||
<img src="<?php print_unescaped(OCP\image_path('core', 'places/home.svg'));?>" class="svg" />
|
||||
</a>
|
||||
</div>
|
||||
<?php for($i=0; $i<count($_["breadcrumb"]); $i++):
|
||||
$crumb = $_["breadcrumb"][$i];
|
||||
$dir = \OCP\Util::encodePath($crumb["dir"]); ?>
|
||||
|
||||
@@ -18,7 +18,7 @@ $totalsize = 0; ?>
|
||||
data-size="<?php p($file['size']);?>"
|
||||
data-etag="<?php p($file['etag']);?>"
|
||||
data-permissions="<?php p($file['permissions']); ?>">
|
||||
<?php if($file['isPreviewAvailable']): ?>
|
||||
<?php if(isset($file['isPreviewAvailable']) and $file['isPreviewAvailable']): ?>
|
||||
<td class="filename svg preview-icon"
|
||||
<?php else: ?>
|
||||
<td class="filename svg"
|
||||
@@ -34,17 +34,15 @@ $totalsize = 0; ?>
|
||||
<span class="nametext">
|
||||
<?php print_unescaped(htmlspecialchars($file['name']));?>
|
||||
</span>
|
||||
<span class="uploadtext" currentUploads="0">
|
||||
</span>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a class="name" href="<?php p(rtrim($_['downloadURL'],'/').'/'.trim($directory,'/').'/'.$name); ?>">
|
||||
<label class="filetext" title="" for="select-<?php p($file['fileid']); ?>"></label>
|
||||
<span class="nametext"><?php print_unescaped(htmlspecialchars($file['basename']));?><span class='extension'><?php p($file['extension']);?></span></span>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if($file['type'] == 'dir'):?>
|
||||
<span class="uploadtext" currentUploads="0">
|
||||
</span>
|
||||
<?php endif;?>
|
||||
</a>
|
||||
</td>
|
||||
<td class="filesize"
|
||||
style="color:rgb(<?php p($simple_size_color.','.$simple_size_color.','.$simple_size_color) ?>)">
|
||||
|
||||
@@ -6,6 +6,7 @@ if (OC::$CLI) {
|
||||
if (count($argv) === 2) {
|
||||
$file = $argv[1];
|
||||
list(, $user) = explode('/', $file);
|
||||
OCP\JSON::checkUserExists($owner);
|
||||
OC_Util::setupFS($user);
|
||||
$view = new \OC\Files\View('');
|
||||
/**
|
||||
|
||||
@@ -13,16 +13,14 @@ use OCA\Encryption\Util;
|
||||
$loginname = isset($_POST['user']) ? $_POST['user'] : '';
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : '';
|
||||
|
||||
$migrationCompleted = true;
|
||||
$migrationStatus = Util::MIGRATION_COMPLETED;
|
||||
|
||||
if ($loginname !== '' && $password !== '') {
|
||||
$username = \OCP\User::checkPassword($loginname, $password);
|
||||
if ($username) {
|
||||
$util = new Util(new \OC_FilesystemView('/'), $username);
|
||||
if ($util->getMigrationStatus() !== Util::MIGRATION_COMPLETED) {
|
||||
$migrationCompleted = false;
|
||||
}
|
||||
$migrationStatus = $util->getMigrationStatus();
|
||||
}
|
||||
}
|
||||
|
||||
\OCP\JSON::success(array('data' => array('migrationCompleted' => $migrationCompleted)));
|
||||
\OCP\JSON::success(array('data' => array('migrationStatus' => $migrationStatus)));
|
||||
|
||||
@@ -10,6 +10,7 @@ OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php';
|
||||
OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php';
|
||||
OC::$CLASSPATH['OCA\Encryption\Helper'] = 'files_encryption/lib/helper.php';
|
||||
|
||||
\OCP\Util::addscript('files_encryption', 'encryption');
|
||||
\OCP\Util::addscript('files_encryption', 'detect-migration');
|
||||
|
||||
if (!OC_Config::getValue('maintenance', false)) {
|
||||
|
||||
@@ -18,22 +18,19 @@
|
||||
<type>text</type>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
<comments>What client-side / server-side configuration is used</comments>
|
||||
</field>
|
||||
<field>
|
||||
<name>recovery_enabled</name>
|
||||
<type>integer</type>
|
||||
<notnull>true</notnull>
|
||||
<default>0</default>
|
||||
<comments>Whether encryption key recovery is enabled</comments>
|
||||
</field>
|
||||
<field>
|
||||
<name>migration_status</name>
|
||||
<type>integer</type>
|
||||
<notnull>true</notnull>
|
||||
<default>0</default>
|
||||
<comments>Whether encryption migration has been performed</comments>
|
||||
</field>
|
||||
</declaration>
|
||||
</table>
|
||||
</database>
|
||||
</database>
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
<info>
|
||||
<id>files_encryption</id>
|
||||
<name>Encryption</name>
|
||||
<description>The new ownCloud 5 files encryption system. After the app was enabled you need to re-login to initialize your encryption keys.</description>
|
||||
<description>The ownCloud files encryption system provides server side-encryption. After the app was enabled you need to re-login to initialize your encryption keys.</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>Sam Tuke, Bjoern Schiessle, Florin Peter</author>
|
||||
<require>4</require>
|
||||
<shipped>true</shipped>
|
||||
<documentation>
|
||||
<user>http://doc.owncloud.org/server/6.0/user_manual/files/encryption.html</user>
|
||||
<admin>http://doc.owncloud.org/server/6.0/admin_manual/configuration/configuration_encryption.html</admin>
|
||||
</documentation>
|
||||
<rememberlogin>false</rememberlogin>
|
||||
<types>
|
||||
<filesystem/>
|
||||
|
||||
@@ -30,6 +30,11 @@ use OC\Files\Filesystem;
|
||||
*/
|
||||
class Hooks {
|
||||
|
||||
// file for which we want to rename the keys after the rename operation was successful
|
||||
private static $renamedFiles = array();
|
||||
// file for which we want to delete the keys after the delete operation was successful
|
||||
private static $deleteFiles = array();
|
||||
|
||||
/**
|
||||
* @brief Startup encryption backend upon user login
|
||||
* @note This method should never be called for users using client side encryption
|
||||
@@ -75,8 +80,14 @@ class Hooks {
|
||||
|
||||
// Check if first-run file migration has already been performed
|
||||
$ready = false;
|
||||
if ($util->getMigrationStatus() === Util::MIGRATION_OPEN) {
|
||||
$migrationStatus = $util->getMigrationStatus();
|
||||
if ($migrationStatus === Util::MIGRATION_OPEN && $session !== false) {
|
||||
$ready = $util->beginMigration();
|
||||
} elseif ($migrationStatus === Util::MIGRATION_IN_PROGRESS) {
|
||||
// refuse login as long as the initial encryption is running
|
||||
sleep(5);
|
||||
\OCP\User::logout();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If migration not yet done
|
||||
@@ -90,28 +101,32 @@ class Hooks {
|
||||
$userView->file_exists('encryption.key')
|
||||
&& $encLegacyKey = $userView->file_get_contents('encryption.key')
|
||||
) {
|
||||
$plainLegacyKey = Crypt::legacyDecrypt($encLegacyKey, $params['password']);
|
||||
|
||||
$plainLegacyKey = Crypt::legacyDecrypt($encLegacyKey, $params['password']);
|
||||
|
||||
$session->setLegacyKey($plainLegacyKey);
|
||||
|
||||
$session->setLegacyKey($plainLegacyKey);
|
||||
}
|
||||
|
||||
// Encrypt existing user files:
|
||||
if (
|
||||
$util->encryptAll('/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password'])
|
||||
) {
|
||||
// Encrypt existing user files
|
||||
try {
|
||||
$result = $util->encryptAll('/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password']);
|
||||
} catch (\Exception $ex) {
|
||||
\OCP\Util::writeLog('Encryption library', 'Initial encryption failed! Error: ' . $ex->getMessage(), \OCP\Util::FATAL);
|
||||
$util->resetMigrationStatus();
|
||||
\OCP\User::logout();
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
|
||||
\OC_Log::write(
|
||||
'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed'
|
||||
, \OC_Log::INFO
|
||||
);
|
||||
|
||||
// Register successful migration in DB
|
||||
$util->finishMigration();
|
||||
|
||||
}
|
||||
|
||||
// Register successful migration in DB
|
||||
$util->finishMigration();
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -179,9 +194,9 @@ class Hooks {
|
||||
// the necessary keys)
|
||||
if (Crypt::mode() === 'server') {
|
||||
|
||||
if ($params['uid'] === \OCP\User::getUser()) {
|
||||
$view = new \OC_FilesystemView('/');
|
||||
|
||||
$view = new \OC_FilesystemView('/');
|
||||
if ($params['uid'] === \OCP\User::getUser()) {
|
||||
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
|
||||
@@ -202,36 +217,45 @@ class Hooks {
|
||||
} else { // admin changed the password for a different user, create new keys and reencrypt file keys
|
||||
|
||||
$user = $params['uid'];
|
||||
$recoveryPassword = $params['recoveryPassword'];
|
||||
$newUserPassword = $params['password'];
|
||||
$util = new Util($view, $user);
|
||||
$recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null;
|
||||
|
||||
$view = new \OC_FilesystemView('/');
|
||||
// we generate new keys if...
|
||||
// ...we have a recovery password and the user enabled the recovery key
|
||||
// ...encryption was activated for the first time (no keys exists)
|
||||
// ...the user doesn't have any files
|
||||
if (($util->recoveryEnabledForUser() && $recoveryPassword)
|
||||
|| !$util->userKeysExists()
|
||||
|| !$view->file_exists($user . '/files')) {
|
||||
|
||||
// make sure that the users home is mounted
|
||||
\OC\Files\Filesystem::initMountPoints($user);
|
||||
$newUserPassword = $params['password'];
|
||||
|
||||
$keypair = Crypt::createKeypair();
|
||||
// make sure that the users home is mounted
|
||||
\OC\Files\Filesystem::initMountPoints($user);
|
||||
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
$keypair = Crypt::createKeypair();
|
||||
|
||||
// Save public key
|
||||
$view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']);
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
// Encrypt private key empty passphrase
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword);
|
||||
// Save public key
|
||||
$view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']);
|
||||
|
||||
// Save private key
|
||||
$view->file_put_contents(
|
||||
'/' . $user . '/files_encryption/' . $user . '.private.key', $encryptedPrivateKey);
|
||||
// Encrypt private key empty passphrase
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword);
|
||||
|
||||
if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
|
||||
$util = new Util($view, $user);
|
||||
$util->recoverUsersFiles($recoveryPassword);
|
||||
// Save private key
|
||||
$view->file_put_contents(
|
||||
'/' . $user . '/files_encryption/' . $user . '.private.key', $encryptedPrivateKey);
|
||||
|
||||
if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
|
||||
$util = new Util($view, $user);
|
||||
$util->recoverUsersFiles($recoveryPassword);
|
||||
}
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
}
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,6 +498,27 @@ class Hooks {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief mark file as renamed so that we know the original source after the file was renamed
|
||||
* @param array $params with the old path and the new path
|
||||
*/
|
||||
public static function preRename($params) {
|
||||
$user = \OCP\User::getUser();
|
||||
$view = new \OC_FilesystemView('/');
|
||||
$util = new Util($view, $user);
|
||||
list($ownerOld, $pathOld) = $util->getUidAndFilename($params['oldpath']);
|
||||
|
||||
// we only need to rename the keys if the rename happens on the same mountpoint
|
||||
// otherwise we perform a stream copy, so we get a new set of keys
|
||||
$mp1 = $view->getMountPoint('/' . $user . '/files/' . $params['oldpath']);
|
||||
$mp2 = $view->getMountPoint('/' . $user . '/files/' . $params['newpath']);
|
||||
if ($mp1 === $mp2) {
|
||||
self::$renamedFiles[$params['oldpath']] = array(
|
||||
'uid' => $ownerOld,
|
||||
'path' => $pathOld);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief after a file is renamed, rename its keyfile and share-keys also fix the file size and fix also the sharing
|
||||
* @param array with oldpath and newpath
|
||||
@@ -496,19 +541,32 @@ class Hooks {
|
||||
$userId = \OCP\User::getUser();
|
||||
$util = new Util($view, $userId);
|
||||
|
||||
// Format paths to be relative to user files dir
|
||||
if ($util->isSystemWideMountPoint($params['oldpath'])) {
|
||||
$baseDir = 'files_encryption/';
|
||||
$oldKeyfilePath = $baseDir . 'keyfiles/' . $params['oldpath'];
|
||||
if (isset(self::$renamedFiles[$params['oldpath']]['uid']) &&
|
||||
isset(self::$renamedFiles[$params['oldpath']]['path'])) {
|
||||
$ownerOld = self::$renamedFiles[$params['oldpath']]['uid'];
|
||||
$pathOld = self::$renamedFiles[$params['oldpath']]['path'];
|
||||
} else {
|
||||
$baseDir = $userId . '/' . 'files_encryption/';
|
||||
$oldKeyfilePath = $baseDir . 'keyfiles/' . $params['oldpath'];
|
||||
\OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($util->isSystemWideMountPoint($params['newpath'])) {
|
||||
$newKeyfilePath = $baseDir . 'keyfiles/' . $params['newpath'];
|
||||
list($ownerNew, $pathNew) = $util->getUidAndFilename($params['newpath']);
|
||||
|
||||
// Format paths to be relative to user files dir
|
||||
if ($util->isSystemWideMountPoint($pathOld)) {
|
||||
$oldKeyfilePath = 'files_encryption/keyfiles/' . $pathOld;
|
||||
$oldShareKeyPath = 'files_encryption/share-keys/' . $pathOld;
|
||||
} else {
|
||||
$newKeyfilePath = $baseDir . 'keyfiles/' . $params['newpath'];
|
||||
$oldKeyfilePath = $ownerOld . '/' . 'files_encryption/keyfiles/' . $pathOld;
|
||||
$oldShareKeyPath = $ownerOld . '/' . 'files_encryption/share-keys/' . $pathOld;
|
||||
}
|
||||
|
||||
if ($util->isSystemWideMountPoint($pathNew)) {
|
||||
$newKeyfilePath = 'files_encryption/keyfiles/' . $pathNew;
|
||||
$newShareKeyPath = 'files_encryption/share-keys/' . $pathNew;
|
||||
} else {
|
||||
$newKeyfilePath = $ownerNew . '/files_encryption/keyfiles/' . $pathNew;
|
||||
$newShareKeyPath = $ownerNew . '/files_encryption/share-keys/' . $pathNew;
|
||||
}
|
||||
|
||||
// add key ext if this is not an folder
|
||||
@@ -517,11 +575,11 @@ class Hooks {
|
||||
$newKeyfilePath .= '.key';
|
||||
|
||||
// handle share-keys
|
||||
$localKeyPath = $view->getLocalFile($baseDir . 'share-keys/' . $params['oldpath']);
|
||||
$localKeyPath = $view->getLocalFile($oldShareKeyPath);
|
||||
$escapedPath = Helper::escapeGlobPattern($localKeyPath);
|
||||
$matches = glob($escapedPath . '*.shareKey');
|
||||
foreach ($matches as $src) {
|
||||
$dst = \OC\Files\Filesystem::normalizePath(str_replace($params['oldpath'], $params['newpath'], $src));
|
||||
$dst = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $src));
|
||||
|
||||
// create destination folder if not exists
|
||||
if (!file_exists(dirname($dst))) {
|
||||
@@ -533,15 +591,13 @@ class Hooks {
|
||||
|
||||
} else {
|
||||
// handle share-keys folders
|
||||
$oldShareKeyfilePath = $baseDir . 'share-keys/' . $params['oldpath'];
|
||||
$newShareKeyfilePath = $baseDir . 'share-keys/' . $params['newpath'];
|
||||
|
||||
// create destination folder if not exists
|
||||
if (!$view->file_exists(dirname($newShareKeyfilePath))) {
|
||||
$view->mkdir(dirname($newShareKeyfilePath), 0750, true);
|
||||
if (!$view->file_exists(dirname($newShareKeyPath))) {
|
||||
mkdir($view->getLocalFile($newShareKeyPath), 0750, true);
|
||||
}
|
||||
|
||||
$view->rename($oldShareKeyfilePath, $newShareKeyfilePath);
|
||||
$view->rename($oldShareKeyPath, $newShareKeyPath);
|
||||
}
|
||||
|
||||
// Rename keyfile so it isn't orphaned
|
||||
@@ -549,25 +605,24 @@ class Hooks {
|
||||
|
||||
// create destination folder if not exists
|
||||
if (!$view->file_exists(dirname($newKeyfilePath))) {
|
||||
$view->mkdir(dirname($newKeyfilePath), 0750, true);
|
||||
mkdir(dirname($view->getLocalFile($newKeyfilePath)), 0750, true);
|
||||
}
|
||||
|
||||
$view->rename($oldKeyfilePath, $newKeyfilePath);
|
||||
}
|
||||
|
||||
// build the path to the file
|
||||
$newPath = '/' . $userId . '/files' . $params['newpath'];
|
||||
$newPathRelative = $params['newpath'];
|
||||
$newPath = '/' . $ownerNew . '/files' . $pathNew;
|
||||
|
||||
if ($util->fixFileSize($newPath)) {
|
||||
// get sharing app state
|
||||
$sharingEnabled = \OCP\Share::isEnabled();
|
||||
|
||||
// get users
|
||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $newPathRelative);
|
||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $pathNew);
|
||||
|
||||
// update sharing-keys
|
||||
$util->setSharedFileKeyfiles($session, $usersSharing, $newPathRelative);
|
||||
$util->setSharedFileKeyfiles($session, $usersSharing, $pathNew);
|
||||
}
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
@@ -600,4 +655,66 @@ class Hooks {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief if the file was really deleted we remove the encryption keys
|
||||
* @param array $params
|
||||
* @return boolean
|
||||
*/
|
||||
public static function postDelete($params) {
|
||||
|
||||
if (!isset(self::$deleteFiles[$params[\OC\Files\Filesystem::signal_param_path]])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$deletedFile = self::$deleteFiles[$params[\OC\Files\Filesystem::signal_param_path]];
|
||||
$path = $deletedFile['path'];
|
||||
$user = $deletedFile['uid'];
|
||||
|
||||
// we don't need to remember the file any longer
|
||||
unset(self::$deleteFiles[$params[\OC\Files\Filesystem::signal_param_path]]);
|
||||
|
||||
$view = new \OC\Files\View('/');
|
||||
|
||||
// return if the file still exists and wasn't deleted correctly
|
||||
if ($view->file_exists('/' . $user . '/files/' . $path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
// Delete keyfile & shareKey so it isn't orphaned
|
||||
if (!Keymanager::deleteFileKey($view, $path, $user)) {
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
'Keyfile or shareKey could not be deleted for file "' . $user.'/files/'.$path . '"', \OCP\Util::ERROR);
|
||||
}
|
||||
|
||||
Keymanager::delAllShareKeys($view, $user, $path);
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief remember the file which should be deleted and it's owner
|
||||
* @param array $params
|
||||
* @return boolean
|
||||
*/
|
||||
public static function preDelete($params) {
|
||||
$path = $params[\OC\Files\Filesystem::signal_param_path];
|
||||
|
||||
// skip this method if the trash bin is enabled or if we delete a file
|
||||
// outside of /data/user/files
|
||||
if (\OCP\App::isEnabled('files_trashbin')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$util = new Util(new \OC_FilesystemView('/'), \OCP\USER::getUser());
|
||||
list($owner, $ownerPath) = $util->getUidAndFilename($path);
|
||||
|
||||
self::$deleteFiles[$params[\OC\Files\Filesystem::signal_param_path]] = array(
|
||||
'uid' => $owner,
|
||||
'path' => $ownerPath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,10 +17,14 @@ $(document).ready(function(){
|
||||
data: {user: user, password: password},
|
||||
async: false,
|
||||
success: function(response) {
|
||||
if (response.data.migrationCompleted === false) {
|
||||
if (response.data.migrationStatus === OC.Encryption.MIGRATION_OPEN) {
|
||||
var message = t('files_encryption', 'Initial encryption started... This can take some time. Please wait.');
|
||||
$('#messageText').text(message);
|
||||
$('#message').removeClass('hidden').addClass('update');
|
||||
} else if (response.data.migrationStatus === OC.Encryption.MIGRATION_IN_PROGRESS) {
|
||||
var message = t('files_encryption', 'Initial encryption running... Please try again later.');
|
||||
$('#messageText').text(message);
|
||||
$('#message').removeClass('hidden').addClass('update');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) 2014
|
||||
* Bjoern Schiessle <schiessle@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
OC.Encryption={
|
||||
MIGRATION_OPEN:0,
|
||||
MIGRATION_COMPLETED:1,
|
||||
MIGRATION_IN_PROGRESS:-1,
|
||||
};
|
||||
@@ -29,6 +29,8 @@ namespace OCA\Encryption;
|
||||
*/
|
||||
class Helper {
|
||||
|
||||
private static $tmpFileMapping; // Map tmp files to files in data/user/files
|
||||
|
||||
/**
|
||||
* @brief register share related hooks
|
||||
*
|
||||
@@ -59,7 +61,10 @@ class Helper {
|
||||
*/
|
||||
public static function registerFilesystemHooks() {
|
||||
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Encryption\Hooks', 'preRename');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_delete', 'OCA\Encryption\Hooks', 'postDelete');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Encryption\Hooks', 'preDelete');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,7 +279,7 @@ class Helper {
|
||||
$split = explode('/', $trimmed);
|
||||
|
||||
// it is not a file relative to data/user/files
|
||||
if (count($split) < 2 || $split[1] !== 'files') {
|
||||
if (count($split) < 2 || ($split[1] !== 'files' && $split[1] !== 'cache')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -370,7 +375,14 @@ class Helper {
|
||||
* @return bool true if requirements are met
|
||||
*/
|
||||
public static function checkRequirements() {
|
||||
return extension_loaded('openssl');
|
||||
$result = true;
|
||||
|
||||
//openssl extension needs to be loaded
|
||||
$result &= extension_loaded("openssl");
|
||||
// we need php >= 5.3.3
|
||||
$result &= version_compare(phpversion(), '5.3.3', '>=');
|
||||
|
||||
return (bool) $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,5 +428,27 @@ class Helper {
|
||||
public static function escapeGlobPattern($path) {
|
||||
return preg_replace('/(\*|\?|\[)/', '[$1]', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief remember from which file the tmp file (getLocalFile() call) was created
|
||||
* @param string $tmpFile path of tmp file
|
||||
* @param string $originalFile path of the original file relative to data/
|
||||
*/
|
||||
public static function addTmpFileToMapper($tmpFile, $originalFile) {
|
||||
self::$tmpFileMapping[$tmpFile] = $originalFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get the path of the original file
|
||||
* @param string $tmpFile path of the tmp file
|
||||
* @return mixed path of the original file or false
|
||||
*/
|
||||
public static function getPathFromTmpFile($tmpFile) {
|
||||
if (isset(self::$tmpFileMapping[$tmpFile])) {
|
||||
return self::$tmpFileMapping[$tmpFile];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,12 @@ class Keymanager {
|
||||
/**
|
||||
* @brief retrieve the ENCRYPTED private key from a user
|
||||
*
|
||||
* @param \OC_FilesystemView $view
|
||||
* @param \OC\Files\View $view
|
||||
* @param string $user
|
||||
* @return string private key or false (hopefully)
|
||||
* @note the key returned by this method must be decrypted before use
|
||||
*/
|
||||
public static function getPrivateKey(\OC_FilesystemView $view, $user) {
|
||||
public static function getPrivateKey($view, $user) {
|
||||
|
||||
$path = '/' . $user . '/' . 'files_encryption' . '/' . $user . '.private.key';
|
||||
$key = false;
|
||||
@@ -214,15 +214,24 @@ class Keymanager {
|
||||
*
|
||||
* @param \OC_FilesystemView $view
|
||||
* @param string $path path of the file the key belongs to
|
||||
* @param string $userId the user to whom the file belongs
|
||||
* @return bool Outcome of unlink operation
|
||||
* @note $path must be relative to data/user/files. e.g. mydoc.txt NOT
|
||||
* /data/admin/files/mydoc.txt
|
||||
*/
|
||||
public static function deleteFileKey(\OC_FilesystemView $view, $path) {
|
||||
public static function deleteFileKey($view, $path, $userId=null) {
|
||||
|
||||
$trimmed = ltrim($path, '/');
|
||||
|
||||
$userId = Helper::getUser($path);
|
||||
if ($trimmed === '') {
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
'Can\'t delete file-key empty path given!', \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($userId === null) {
|
||||
$userId = Helper::getUser($path);
|
||||
}
|
||||
$util = new Util($view, $userId);
|
||||
|
||||
if($util->isSystemWideMountPoint($path)) {
|
||||
@@ -402,7 +411,15 @@ class Keymanager {
|
||||
* @param string $userId owner of the file
|
||||
* @param string $filePath path to the file, relative to the owners file dir
|
||||
*/
|
||||
public static function delAllShareKeys(\OC_FilesystemView $view, $userId, $filePath) {
|
||||
public static function delAllShareKeys($view, $userId, $filePath) {
|
||||
|
||||
$filePath = ltrim($filePath, '/');
|
||||
|
||||
if ($filePath === '') {
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
'Can\'t delete share-keys empty path given!', \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
$util = new util($view, $userId);
|
||||
|
||||
@@ -413,17 +430,15 @@ class Keymanager {
|
||||
}
|
||||
|
||||
|
||||
if ($view->is_dir($userId . '/files/' . $filePath)) {
|
||||
if ($view->is_dir($baseDir . $filePath)) {
|
||||
$view->unlink($baseDir . $filePath);
|
||||
} else {
|
||||
$localKeyPath = $view->getLocalFile($baseDir . $filePath);
|
||||
$escapedPath = Helper::escapeGlobPattern($localKeyPath);
|
||||
$matches = glob($escapedPath . '*.shareKey');
|
||||
foreach ($matches as $ma) {
|
||||
$result = unlink($ma);
|
||||
if (!$result) {
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
'Keyfile or shareKey could not be deleted for file "' . $filePath . '"', \OCP\Util::ERROR);
|
||||
$parentDir = dirname($baseDir . $filePath);
|
||||
$filename = pathinfo($filePath, PATHINFO_BASENAME);
|
||||
foreach($view->getDirectoryContent($parentDir) as $content) {
|
||||
$path = $content['path'];
|
||||
if (self::getFilenameFromShareKey($content['name']) === $filename) {
|
||||
$view->unlink('/' . $userId . '/' . $path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -523,4 +538,20 @@ class Keymanager {
|
||||
return $targetPath;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief extract filename from share key name
|
||||
* @param string $shareKey (filename.userid.sharekey)
|
||||
* @return mixed filename or false
|
||||
*/
|
||||
protected static function getFilenameFromShareKey($shareKey) {
|
||||
$parts = explode('.', $shareKey);
|
||||
|
||||
$filename = false;
|
||||
if(count($parts) > 2) {
|
||||
$filename = implode('.', array_slice($parts, 0, count($parts)-2));
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ namespace OCA\Encryption;
|
||||
class Proxy extends \OC_FileProxy {
|
||||
|
||||
private static $blackList = null; //mimetypes blacklisted from encryption
|
||||
private static $unencryptedSizes = array(); // remember unencrypted size
|
||||
private static $fopenMode = array(); // remember the fopen mode
|
||||
|
||||
/**
|
||||
* Check if a file requires encryption
|
||||
@@ -114,11 +116,20 @@ class Proxy extends \OC_FileProxy {
|
||||
// get encrypted content
|
||||
$data = $view->file_get_contents($tmpPath);
|
||||
|
||||
// store new unenecrypted size so that it can be updated
|
||||
// in the post proxy
|
||||
$tmpFileInfo = $view->getFileInfo($tmpPath);
|
||||
if ( isset($tmpFileInfo['size']) ) {
|
||||
self::$unencryptedSizes[\OC_Filesystem::normalizePath($path)] = $tmpFileInfo['size'];
|
||||
}
|
||||
|
||||
// remove our temp file
|
||||
$view->deleteAll('/' . \OCP\User::getUser() . '/cache/' . $cacheFolder);
|
||||
|
||||
// re-enable proxy - our work is done
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,6 +138,24 @@ class Proxy extends \OC_FileProxy {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief update file cache with the new unencrypted size after file was written
|
||||
* @param string $path
|
||||
* @param mixed $result
|
||||
* @return mixed
|
||||
*/
|
||||
public function postFile_put_contents($path, $result) {
|
||||
$normalizedPath = \OC_Filesystem::normalizePath($path);
|
||||
if ( isset(self::$unencryptedSizes[$normalizedPath]) ) {
|
||||
$view = new \OC_FilesystemView('/');
|
||||
$view->putFileInfo($normalizedPath,
|
||||
array('encrypted' => true, 'unencrypted_size' => self::$unencryptedSizes[$normalizedPath]));
|
||||
unset(self::$unencryptedSizes[$normalizedPath]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path Path of file from which has been read
|
||||
* @param string $data Data that has been read from file
|
||||
@@ -177,47 +206,6 @@ class Proxy extends \OC_FileProxy {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief When a file is deleted, remove its keyfile also
|
||||
*/
|
||||
public function preUnlink($path) {
|
||||
|
||||
// let the trashbin handle this
|
||||
if (\OCP\App::isEnabled('files_trashbin')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$view = new \OC_FilesystemView('/');
|
||||
|
||||
$userId = \OCP\USER::getUser();
|
||||
|
||||
$util = new Util($view, $userId);
|
||||
|
||||
// get relative path
|
||||
$relativePath = \OCA\Encryption\Helper::stripUserFilesPath($path);
|
||||
|
||||
list($owner, $ownerPath) = $util->getUidAndFilename($relativePath);
|
||||
|
||||
// Delete keyfile & shareKey so it isn't orphaned
|
||||
if (!Keymanager::deleteFileKey($view, $ownerPath)) {
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
'Keyfile or shareKey could not be deleted for file "' . $ownerPath . '"', \OCP\Util::ERROR);
|
||||
}
|
||||
|
||||
Keymanager::delAllShareKeys($view, $owner, $ownerPath);
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
// If we don't return true then file delete will fail; better
|
||||
// to leave orphaned keyfiles than to disallow file deletion
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @return bool
|
||||
@@ -228,6 +216,16 @@ class Proxy extends \OC_FileProxy {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief remember initial fopen mode because sometimes it gets changed during the request
|
||||
* @param string $path path
|
||||
* @param string $mode type of access
|
||||
*/
|
||||
public function preFopen($path, $mode) {
|
||||
self::$fopenMode[$path] = $mode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param $result
|
||||
@@ -246,8 +244,8 @@ class Proxy extends \OC_FileProxy {
|
||||
// split the path parts
|
||||
$pathParts = explode('/', $path);
|
||||
|
||||
// FIXME: handling for /userId/cache used by webdav for chunking. The cache chunks are NOT encrypted
|
||||
if (isset($pathParts[2]) && $pathParts[2] === 'cache') {
|
||||
// don't try to encrypt/decrypt cache chunks or files in the trash bin
|
||||
if (isset($pathParts[2]) && ($pathParts[2] === 'cache' || $pathParts[2] === 'files_trashbin')) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -255,7 +253,15 @@ class Proxy extends \OC_FileProxy {
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$meta = stream_get_meta_data($result);
|
||||
// if we remember the mode from the pre proxy we re-use it
|
||||
// oterwise we fall back to stream_get_meta_data()
|
||||
if (isset(self::$fopenMode[$path])) {
|
||||
$mode = self::$fopenMode[$path];
|
||||
unset(self::$fopenMode[$path]);
|
||||
} else {
|
||||
$meta = stream_get_meta_data($result);
|
||||
$mode = $meta['mode'];
|
||||
}
|
||||
|
||||
$view = new \OC_FilesystemView('');
|
||||
|
||||
@@ -273,14 +279,14 @@ class Proxy extends \OC_FileProxy {
|
||||
|
||||
// Open the file using the crypto stream wrapper
|
||||
// protocol and let it do the decryption work instead
|
||||
$result = fopen('crypt://' . $path, $meta['mode']);
|
||||
$result = fopen('crypt://' . $path, $mode);
|
||||
|
||||
} elseif (
|
||||
self::shouldEncrypt($path)
|
||||
and $meta ['mode'] !== 'r'
|
||||
and $meta['mode'] !== 'rb'
|
||||
self::shouldEncrypt($path)
|
||||
and $mode !== 'r'
|
||||
and $mode !== 'rb'
|
||||
) {
|
||||
$result = fopen('crypt://' . $path, $meta['mode']);
|
||||
$result = fopen('crypt://' . $path, $mode);
|
||||
}
|
||||
|
||||
// Re-enable the proxy
|
||||
@@ -305,7 +311,7 @@ class Proxy extends \OC_FileProxy {
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
// get file size
|
||||
$data['size'] = self::postFileSize($path, $data['size']);
|
||||
$data['size'] = self::postFileSize($path, $data['size'], $data);
|
||||
|
||||
// Re-enable the proxy
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
@@ -319,7 +325,7 @@ class Proxy extends \OC_FileProxy {
|
||||
* @param $size
|
||||
* @return bool
|
||||
*/
|
||||
public function postFileSize($path, $size) {
|
||||
public function postFileSize($path, $size, $fileInfo = null) {
|
||||
|
||||
$view = new \OC_FilesystemView('/');
|
||||
|
||||
@@ -335,6 +341,13 @@ class Proxy extends \OC_FileProxy {
|
||||
|
||||
// if path is a folder do nothing
|
||||
if ($view->is_dir($path)) {
|
||||
$proxyState = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
$fileInfo = $view->getFileInfo($path);
|
||||
\OC_FileProxy::$enabled = $proxyState;
|
||||
if (isset($fileInfo['unencrypted_size']) && $fileInfo['unencrypted_size'] > 0) {
|
||||
return $fileInfo['unencrypted_size'];
|
||||
}
|
||||
return $size;
|
||||
}
|
||||
|
||||
@@ -346,9 +359,8 @@ class Proxy extends \OC_FileProxy {
|
||||
return $size;
|
||||
}
|
||||
|
||||
$fileInfo = false;
|
||||
// get file info from database/cache if not .part file
|
||||
if (!Helper::isPartialFilePath($path)) {
|
||||
if (empty($fileInfo) && !Helper::isPartialFilePath($path)) {
|
||||
$proxyState = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
$fileInfo = $view->getFileInfo($path);
|
||||
@@ -356,7 +368,7 @@ class Proxy extends \OC_FileProxy {
|
||||
}
|
||||
|
||||
// if file is encrypted return real file size
|
||||
if (is_array($fileInfo) && $fileInfo['encrypted'] === true) {
|
||||
if (isset($fileInfo['encrypted']) && $fileInfo['encrypted'] === true) {
|
||||
// try to fix unencrypted file size if it doesn't look plausible
|
||||
if ((int)$fileInfo['size'] > 0 && (int)$fileInfo['unencrypted_size'] === 0 ) {
|
||||
$fixSize = $util->getFileSize($path);
|
||||
|
||||
@@ -132,6 +132,14 @@ class Session {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief remove encryption keys and init status from session
|
||||
*/
|
||||
public function closeSession() {
|
||||
\OC::$session->remove('encryptionInitialized');
|
||||
\OC::$session->remove('privateKey');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Gets status if we already tried to initialize the encryption app
|
||||
|
||||
@@ -64,6 +64,9 @@ class Stream {
|
||||
private $publicKey;
|
||||
private $encKeyfile;
|
||||
private $newFile; // helper var, we only need to write the keyfile for new files
|
||||
private $isLocalTmpFile = false; // do we operate on a local tmp file
|
||||
private $localTmpFile; // path of local tmp file
|
||||
|
||||
/**
|
||||
* @var \OC\Files\View
|
||||
*/
|
||||
@@ -91,13 +94,18 @@ class Stream {
|
||||
$this->rootView = new \OC_FilesystemView('/');
|
||||
}
|
||||
|
||||
|
||||
$this->session = new \OCA\Encryption\Session($this->rootView);
|
||||
|
||||
$this->privateKey = $this->session->getPrivateKey();
|
||||
|
||||
// rawPath is relative to the data directory
|
||||
$this->rawPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path));
|
||||
$normalizedPath = \OC\Files\Filesystem::normalizePath(str_replace('crypt://', '', $path));
|
||||
if ($originalFile = Helper::getPathFromTmpFile($normalizedPath)) {
|
||||
$this->rawPath = $originalFile;
|
||||
$this->isLocalTmpFile = true;
|
||||
$this->localTmpFile = $normalizedPath;
|
||||
} else {
|
||||
$this->rawPath = $normalizedPath;
|
||||
}
|
||||
|
||||
$this->userId = Helper::getUser($this->rawPath);
|
||||
|
||||
@@ -141,10 +149,14 @@ class Stream {
|
||||
\OCA\Encryption\Helper::redirectToErrorPage($this->session);
|
||||
}
|
||||
|
||||
$this->size = $this->rootView->filesize($this->rawPath, $mode);
|
||||
$this->size = $this->rootView->filesize($this->rawPath);
|
||||
}
|
||||
|
||||
$this->handle = $this->rootView->fopen($this->rawPath, $mode);
|
||||
if ($this->isLocalTmpFile) {
|
||||
$this->handle = fopen($this->localTmpFile, $mode);
|
||||
} else {
|
||||
$this->handle = $this->rootView->fopen($this->rawPath, $mode);
|
||||
}
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
@@ -155,6 +167,9 @@ class Stream {
|
||||
} else {
|
||||
|
||||
$this->meta = stream_get_meta_data($this->handle);
|
||||
// sometimes fopen changes the mode, e.g. for a url "r" convert to "r+"
|
||||
// but we need to remember the original access type
|
||||
$this->meta['mode'] = $mode;
|
||||
|
||||
}
|
||||
|
||||
@@ -163,15 +178,26 @@ class Stream {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the current position of the file pointer
|
||||
* @return int position of the file pointer
|
||||
*/
|
||||
public function stream_tell() {
|
||||
return ftell($this->handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $offset
|
||||
* @param int $whence
|
||||
* @return bool true if fseek was successful, otherwise false
|
||||
*/
|
||||
public function stream_seek($offset, $whence = SEEK_SET) {
|
||||
|
||||
$this->flush();
|
||||
|
||||
fseek($this->handle, $offset, $whence);
|
||||
// this wrapper needs to return "true" for success.
|
||||
// the fseek call itself returns 0 on succeess
|
||||
return !fseek($this->handle, $offset, $whence);
|
||||
|
||||
}
|
||||
|
||||
@@ -477,7 +503,7 @@ class Stream {
|
||||
if ($this->privateKey === false) {
|
||||
|
||||
// cleanup
|
||||
if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb') {
|
||||
if ($this->meta['mode'] !== 'r' && $this->meta['mode'] !== 'rb' && !$this->isLocalTmpFile) {
|
||||
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
@@ -498,6 +524,7 @@ class Stream {
|
||||
if (
|
||||
$this->meta['mode'] !== 'r' &&
|
||||
$this->meta['mode'] !== 'rb' &&
|
||||
$this->isLocalTmpFile === false &&
|
||||
$this->size > 0 &&
|
||||
$this->unencryptedSize > 0
|
||||
) {
|
||||
@@ -518,7 +545,7 @@ class Stream {
|
||||
$util = new Util($this->rootView, $this->userId);
|
||||
|
||||
// Get all users sharing the file includes current user
|
||||
$uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $this->relPath, $this->userId);
|
||||
$uniqueUserIds = $util->getSharingUsersArray($sharingEnabled, $this->relPath);
|
||||
$checkedUserIds = $util->filterShareReadyUsers($uniqueUserIds);
|
||||
|
||||
// Fetch public keys for all sharing users
|
||||
|
||||
+181
-145
@@ -2,9 +2,10 @@
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Sam Tuke, Frank Karlitschek
|
||||
* @author Sam Tuke, Frank Karlitschek, Bjoern Schiessle
|
||||
* @copyright 2012 Sam Tuke <samtuke@owncloud.com>,
|
||||
* Frank Karlitschek <frank@owncloud.org>
|
||||
* Frank Karlitschek <frank@owncloud.org>,
|
||||
* Bjoern Schiessle <schiessle@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
@@ -56,7 +57,7 @@ class Util {
|
||||
* @param $userId
|
||||
* @param bool $client
|
||||
*/
|
||||
public function __construct(\OC_FilesystemView $view, $userId, $client = false) {
|
||||
public function __construct($view, $userId, $client = false) {
|
||||
|
||||
$this->view = $view;
|
||||
$this->client = $client;
|
||||
@@ -101,15 +102,24 @@ class Util {
|
||||
or !$this->view->file_exists($this->publicKeyPath)
|
||||
or !$this->view->file_exists($this->privateKeyPath)
|
||||
) {
|
||||
|
||||
return false;
|
||||
|
||||
} else {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief check if the users private & public key exists
|
||||
* @return boolean
|
||||
*/
|
||||
public function userKeysExists() {
|
||||
if (
|
||||
$this->view->file_exists($this->privateKeyPath) &&
|
||||
$this->view->file_exists($this->publicKeyPath)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,7 +133,6 @@ class Util {
|
||||
// Set directories to check / create
|
||||
$setUpDirs = array(
|
||||
$this->userDir,
|
||||
$this->userFilesDir,
|
||||
$this->publicKeyDir,
|
||||
$this->encryptionDir,
|
||||
$this->keyfilesPath,
|
||||
@@ -232,11 +241,9 @@ class Util {
|
||||
if (\OCP\DB::isError($result)) {
|
||||
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
||||
} else {
|
||||
if ($result->numRows() > 0) {
|
||||
$row = $result->fetchRow();
|
||||
if (isset($row['recovery_enabled'])) {
|
||||
$recoveryEnabled[] = $row['recovery_enabled'];
|
||||
}
|
||||
$row = $result->fetchRow();
|
||||
if ($row && isset($row['recovery_enabled'])) {
|
||||
$recoveryEnabled[] = $row['recovery_enabled'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +287,7 @@ class Util {
|
||||
$sql = 'UPDATE `*PREFIX*encryption` SET `recovery_enabled` = ? WHERE `uid` = ?';
|
||||
|
||||
$args = array(
|
||||
$enabled,
|
||||
$enabled ? '1' : '0',
|
||||
$this->userId
|
||||
);
|
||||
|
||||
@@ -308,7 +315,8 @@ class Util {
|
||||
$found = array(
|
||||
'plain' => array(),
|
||||
'encrypted' => array(),
|
||||
'legacy' => array()
|
||||
'legacy' => array(),
|
||||
'broken' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -319,10 +327,7 @@ class Util {
|
||||
if(is_resource($handle)) {
|
||||
while (false !== ($file = readdir($handle))) {
|
||||
|
||||
if (
|
||||
$file !== "."
|
||||
&& $file !== ".."
|
||||
) {
|
||||
if ($file !== "." && $file !== "..") {
|
||||
|
||||
$filePath = $directory . '/' . $this->view->getRelativePath('/' . $file);
|
||||
$relPath = \OCA\Encryption\Helper::stripUserFilesPath($filePath);
|
||||
@@ -349,15 +354,23 @@ class Util {
|
||||
// NOTE: This is inefficient;
|
||||
// scanning every file like this
|
||||
// will eat server resources :(
|
||||
if (
|
||||
Keymanager::getFileKey($this->view, $this, $relPath)
|
||||
&& $isEncryptedPath
|
||||
) {
|
||||
if ($isEncryptedPath) {
|
||||
|
||||
$found['encrypted'][] = array(
|
||||
'name' => $file,
|
||||
'path' => $filePath
|
||||
);
|
||||
$fileKey = Keymanager::getFileKey($this->view, $this, $relPath);
|
||||
$shareKey = Keymanager::getShareKey($this->view, $this->userId, $this, $relPath);
|
||||
// if file is encrypted but now file key is available, throw exception
|
||||
if ($fileKey === false || $shareKey === false) {
|
||||
\OCP\Util::writeLog('encryption library', 'No keys available to decrypt the file: ' . $filePath, \OCP\Util::ERROR);
|
||||
$found['broken'][] = array(
|
||||
'name' => $file,
|
||||
'path' => $filePath,
|
||||
);
|
||||
} else {
|
||||
$found['encrypted'][] = array(
|
||||
'name' => $file,
|
||||
'path' => $filePath,
|
||||
);
|
||||
}
|
||||
|
||||
// If the file uses old
|
||||
// encryption system
|
||||
@@ -455,20 +468,36 @@ class Util {
|
||||
*/
|
||||
public function isEncryptedPath($path) {
|
||||
|
||||
$relPath = Helper::getPathToRealFile($path);
|
||||
// Disable encryption proxy so data retrieved is in its
|
||||
// original form
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
if ($relPath === false) {
|
||||
$relPath = Helper::stripUserFilesPath($path);
|
||||
// we only need 24 byte from the last chunk
|
||||
$data = '';
|
||||
$handle = $this->view->fopen($path, 'r');
|
||||
if (is_resource($handle)) {
|
||||
// suppress fseek warining, we handle the case that fseek doesn't
|
||||
// work in the else branch
|
||||
if (@fseek($handle, -24, SEEK_END) === 0) {
|
||||
$data = fgets($handle);
|
||||
} else {
|
||||
// if fseek failed on the storage we create a local copy from the file
|
||||
// and read this one
|
||||
fclose($handle);
|
||||
$localFile = $this->view->getLocalFile($path);
|
||||
$handle = fopen($localFile, 'r');
|
||||
if (is_resource($handle) && fseek($handle, -24, SEEK_END) === 0) {
|
||||
$data = fgets($handle);
|
||||
}
|
||||
}
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
$fileKey = Keymanager::getFileKey($this->view, $this, $relPath);
|
||||
|
||||
if ($fileKey === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// re-enable proxy
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
return Crypt::isCatfileContent($data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -514,7 +543,20 @@ class Util {
|
||||
$lastChunckPos = ($lastChunkNr * 8192);
|
||||
|
||||
// seek to end
|
||||
fseek($stream, $lastChunckPos);
|
||||
if (@fseek($stream, $lastChunckPos) === -1) {
|
||||
// storage doesn't support fseek, we need a local copy
|
||||
fclose($stream);
|
||||
$localFile = $this->view->getLocalFile($path);
|
||||
Helper::addTmpFileToMapper($localFile, $path);
|
||||
$stream = fopen('crypt://' . $localFile, "r");
|
||||
if (fseek($stream, $lastChunckPos) === -1) {
|
||||
// if fseek also fails on the local storage, than
|
||||
// there is nothing we can do
|
||||
fclose($stream);
|
||||
\OCP\Util::writeLog('Encryption library', 'couldn\'t determine size of "' . $path, \OCP\Util::ERROR);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
// get the content of the last chunk
|
||||
$lastChunkContent = fread($stream, $lastChunkSize);
|
||||
@@ -761,7 +803,7 @@ class Util {
|
||||
\OC\Files\Filesystem::putFileInfo($relPath, array(
|
||||
'encrypted' => false,
|
||||
'size' => $size,
|
||||
'unencrypted_size' => $size,
|
||||
'unencrypted_size' => 0,
|
||||
'etag' => $fileInfo['etag']
|
||||
));
|
||||
|
||||
@@ -777,6 +819,12 @@ class Util {
|
||||
$successful = false;
|
||||
}
|
||||
|
||||
// if there are broken encrypted files than the complete decryption
|
||||
// was not successful
|
||||
if (!empty($found['broken'])) {
|
||||
$successful = false;
|
||||
}
|
||||
|
||||
if ($successful) {
|
||||
$this->view->deleteAll($this->keyfilesPath);
|
||||
$this->view->deleteAll($this->shareKeysPath);
|
||||
@@ -831,32 +879,35 @@ class Util {
|
||||
// Open enc file handle for binary writing, with same filename as original plain file
|
||||
$encHandle = fopen('crypt://' . $rawPath . '.part', 'wb');
|
||||
|
||||
// Move plain file to a temporary location
|
||||
$size = stream_copy_to_stream($plainHandle, $encHandle);
|
||||
if (is_resource($encHandle)) {
|
||||
// Move plain file to a temporary location
|
||||
$size = stream_copy_to_stream($plainHandle, $encHandle);
|
||||
|
||||
fclose($encHandle);
|
||||
fclose($plainHandle);
|
||||
fclose($encHandle);
|
||||
fclose($plainHandle);
|
||||
|
||||
$fakeRoot = $this->view->getRoot();
|
||||
$this->view->chroot('/' . $this->userId . '/files');
|
||||
$fakeRoot = $this->view->getRoot();
|
||||
$this->view->chroot('/' . $this->userId . '/files');
|
||||
|
||||
$this->view->rename($relPath . '.part', $relPath);
|
||||
$this->view->rename($relPath . '.part', $relPath);
|
||||
|
||||
// set timestamp
|
||||
$this->view->touch($relPath, $timestamp);
|
||||
// set timestamp
|
||||
$this->view->touch($relPath, $timestamp);
|
||||
|
||||
$this->view->chroot($fakeRoot);
|
||||
$encSize = $this->view->filesize($relPath);
|
||||
|
||||
// Add the file to the cache
|
||||
\OC\Files\Filesystem::putFileInfo($relPath, array(
|
||||
'encrypted' => true,
|
||||
'size' => $size,
|
||||
'unencrypted_size' => $size,
|
||||
'etag' => $fileInfo['etag']
|
||||
));
|
||||
$this->view->chroot($fakeRoot);
|
||||
|
||||
$encryptedFiles[] = $relPath;
|
||||
// Add the file to the cache
|
||||
\OC\Files\Filesystem::putFileInfo($relPath, array(
|
||||
'encrypted' => true,
|
||||
'size' => $encSize,
|
||||
'unencrypted_size' => $size,
|
||||
'etag' => $fileInfo['etag']
|
||||
));
|
||||
|
||||
$encryptedFiles[] = $relPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt legacy encrypted files
|
||||
@@ -973,8 +1024,8 @@ class Util {
|
||||
if (\OCP\DB::isError($result)) {
|
||||
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
||||
} else {
|
||||
if ($result->numRows() > 0) {
|
||||
$row = $result->fetchRow();
|
||||
$row = $result->fetchRow();
|
||||
if ($row) {
|
||||
$path = substr($row['path'], strlen('files'));
|
||||
}
|
||||
}
|
||||
@@ -1113,8 +1164,10 @@ class Util {
|
||||
/**
|
||||
* @brief Find, sanitise and format users sharing a file
|
||||
* @note This wraps other methods into a portable bundle
|
||||
* @param boolean $sharingEnabled
|
||||
* @param string $filePath path relativ to current users files folder
|
||||
*/
|
||||
public function getSharingUsersArray($sharingEnabled, $filePath, $currentUserId = false) {
|
||||
public function getSharingUsersArray($sharingEnabled, $filePath) {
|
||||
|
||||
// Check if key recovery is enabled
|
||||
if (
|
||||
@@ -1131,12 +1184,14 @@ class Util {
|
||||
|
||||
$ownerPath = \OCA\Encryption\Helper::stripPartialFileExtension($ownerPath);
|
||||
|
||||
$userIds = array();
|
||||
// always add owner to the list of users with access to the file
|
||||
$userIds = array($owner);
|
||||
|
||||
if ($sharingEnabled) {
|
||||
|
||||
// Find out who, if anyone, is sharing the file
|
||||
$result = \OCP\Share::getUsersSharingFile($ownerPath, $owner, true);
|
||||
$userIds = $result['users'];
|
||||
$result = \OCP\Share::getUsersSharingFile($ownerPath, $owner);
|
||||
$userIds = \array_merge($userIds, $result['users']);
|
||||
if ($result['public']) {
|
||||
$userIds[] = $this->publicShareKeyId;
|
||||
}
|
||||
@@ -1152,11 +1207,6 @@ class Util {
|
||||
$userIds[] = $recoveryKeyId;
|
||||
}
|
||||
|
||||
// add current user if given
|
||||
if ($currentUserId !== false) {
|
||||
$userIds[] = $currentUserId;
|
||||
}
|
||||
|
||||
// check if it is a group mount
|
||||
if (\OCP\App::isEnabled("files_external")) {
|
||||
$mount = \OC_Mount_Config::getSystemMountPoints();
|
||||
@@ -1188,27 +1238,49 @@ class Util {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set migration status
|
||||
* @param int $status
|
||||
* @return boolean
|
||||
*/
|
||||
private function setMigrationStatus($status) {
|
||||
|
||||
$sql = 'UPDATE `*PREFIX*encryption` SET `migration_status` = ? WHERE `uid` = ?';
|
||||
$args = array($status, $this->userId);
|
||||
$query = \OCP\DB::prepare($sql);
|
||||
$manipulatedRows = $query->execute($args);
|
||||
|
||||
if ($manipulatedRows === 1) {
|
||||
$result = true;
|
||||
\OCP\Util::writeLog('Encryption library', "Migration status set to " . self::MIGRATION_OPEN, \OCP\Util::INFO);
|
||||
} else {
|
||||
$result = false;
|
||||
\OCP\Util::writeLog('Encryption library', "Could not set migration status to " . self::MIGRATION_OPEN, \OCP\Util::WARN);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief start migration mode to initially encrypt users data
|
||||
* @return boolean
|
||||
*/
|
||||
public function beginMigration() {
|
||||
|
||||
$return = false;
|
||||
$result = $this->setMigrationStatus(self::MIGRATION_IN_PROGRESS);
|
||||
|
||||
$sql = 'UPDATE `*PREFIX*encryption` SET `migration_status` = ? WHERE `uid` = ? and `migration_status` = ?';
|
||||
$args = array(self::MIGRATION_IN_PROGRESS, $this->userId, self::MIGRATION_OPEN);
|
||||
$query = \OCP\DB::prepare($sql);
|
||||
$manipulatedRows = $query->execute($args);
|
||||
|
||||
if ($manipulatedRows === 1) {
|
||||
$return = true;
|
||||
if ($result) {
|
||||
\OCP\Util::writeLog('Encryption library', "Start migration to encryption mode for " . $this->userId, \OCP\Util::INFO);
|
||||
} else {
|
||||
\OCP\Util::writeLog('Encryption library', "Could not activate migration mode for " . $this->userId . ". Probably another process already started the initial encryption", \OCP\Util::WARN);
|
||||
}
|
||||
|
||||
return $return;
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function resetMigrationStatus() {
|
||||
return $this->setMigrationStatus(self::MIGRATION_OPEN);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1216,22 +1288,15 @@ class Util {
|
||||
* @return boolean
|
||||
*/
|
||||
public function finishMigration() {
|
||||
$result = $this->setMigrationStatus(self::MIGRATION_COMPLETED);
|
||||
|
||||
$return = false;
|
||||
|
||||
$sql = 'UPDATE `*PREFIX*encryption` SET `migration_status` = ? WHERE `uid` = ? and `migration_status` = ?';
|
||||
$args = array(self::MIGRATION_COMPLETED, $this->userId, self::MIGRATION_IN_PROGRESS);
|
||||
$query = \OCP\DB::prepare($sql);
|
||||
$manipulatedRows = $query->execute($args);
|
||||
|
||||
if ($manipulatedRows === 1) {
|
||||
$return = true;
|
||||
if ($result) {
|
||||
\OCP\Util::writeLog('Encryption library', "Finish migration successfully for " . $this->userId, \OCP\Util::INFO);
|
||||
} else {
|
||||
\OCP\Util::writeLog('Encryption library', "Could not deactivate migration mode for " . $this->userId, \OCP\Util::WARN);
|
||||
}
|
||||
|
||||
return $return;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1254,11 +1319,9 @@ class Util {
|
||||
if (\OCP\DB::isError($result)) {
|
||||
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
||||
} else {
|
||||
if ($result->numRows() > 0) {
|
||||
$row = $result->fetchRow();
|
||||
if (isset($row['migration_status'])) {
|
||||
$migrationStatus[] = $row['migration_status'];
|
||||
}
|
||||
$row = $result->fetchRow();
|
||||
if ($row && isset($row['migration_status'])) {
|
||||
$migrationStatus[] = $row['migration_status'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1366,59 +1429,32 @@ class Util {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief go recursively through a dir and collect all files and sub files.
|
||||
* @param string $dir relative to the users files folder
|
||||
* @return array with list of files relative to the users files folder
|
||||
*/
|
||||
public function getAllFiles($dir) {
|
||||
|
||||
$result = array();
|
||||
$dirList = array($dir);
|
||||
|
||||
$content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath(
|
||||
$this->userFilesDir . '/' . $dir));
|
||||
|
||||
// handling for re shared folders
|
||||
$pathSplit = explode('/', $dir);
|
||||
|
||||
foreach ($content as $c) {
|
||||
|
||||
$sharedPart = $pathSplit[sizeof($pathSplit) - 1];
|
||||
$targetPathSplit = array_reverse(explode('/', $c['path']));
|
||||
|
||||
$path = '';
|
||||
|
||||
// rebuild path
|
||||
foreach ($targetPathSplit as $pathPart) {
|
||||
|
||||
if ($pathPart !== $sharedPart) {
|
||||
|
||||
$path = '/' . $pathPart . $path;
|
||||
while ($dirList) {
|
||||
$dir = array_pop($dirList);
|
||||
$content = $this->view->getDirectoryContent(\OC\Files\Filesystem::normalizePath(
|
||||
$this->userFilesDir . '/' . $dir));
|
||||
|
||||
foreach ($content as $c) {
|
||||
$usersPath = isset($c['usersPath']) ? $c['usersPath'] : $c['path'];
|
||||
if ($c['type'] === 'dir') {
|
||||
$dirList[] = substr($usersPath, strlen("files"));
|
||||
} else {
|
||||
|
||||
break;
|
||||
|
||||
$result[] = substr($usersPath, strlen("files"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$path = $dir . $path;
|
||||
|
||||
if ($c['type'] === 'dir') {
|
||||
|
||||
$result = array_merge($result, $this->getAllFiles($path));
|
||||
|
||||
} else {
|
||||
|
||||
$result[] = $path;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1438,9 +1474,7 @@ class Util {
|
||||
if (\OCP\DB::isError($result)) {
|
||||
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
||||
} else {
|
||||
if ($result->numRows() > 0) {
|
||||
$row = $result->fetchRow();
|
||||
}
|
||||
$row = $result->fetchRow();
|
||||
}
|
||||
|
||||
return $row;
|
||||
@@ -1464,9 +1498,7 @@ class Util {
|
||||
if (\OCP\DB::isError($result)) {
|
||||
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
||||
} else {
|
||||
if ($result->numRows() > 0) {
|
||||
$row = $result->fetchRow();
|
||||
}
|
||||
$row = $result->fetchRow();
|
||||
}
|
||||
|
||||
return $row;
|
||||
@@ -1485,18 +1517,16 @@ class Util {
|
||||
|
||||
$result = $query->execute(array($id));
|
||||
|
||||
$source = array();
|
||||
$source = null;
|
||||
if (\OCP\DB::isError($result)) {
|
||||
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
||||
} else {
|
||||
if ($result->numRows() > 0) {
|
||||
$source = $result->fetchRow();
|
||||
}
|
||||
$source = $result->fetchRow();
|
||||
}
|
||||
|
||||
$fileOwner = false;
|
||||
|
||||
if (isset($source['parent'])) {
|
||||
if ($source && isset($source['parent'])) {
|
||||
|
||||
$parent = $source['parent'];
|
||||
|
||||
@@ -1506,16 +1536,14 @@ class Util {
|
||||
|
||||
$result = $query->execute(array($parent));
|
||||
|
||||
$item = array();
|
||||
$item = null;
|
||||
if (\OCP\DB::isError($result)) {
|
||||
\OCP\Util::writeLog('Encryption library', \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
||||
} else {
|
||||
if ($result->numRows() > 0) {
|
||||
$item = $result->fetchRow();
|
||||
}
|
||||
$item = $result->fetchRow();
|
||||
}
|
||||
|
||||
if (isset($item['parent'])) {
|
||||
if ($item && isset($item['parent'])) {
|
||||
|
||||
$parent = $item['parent'];
|
||||
|
||||
@@ -1780,4 +1808,12 @@ class Util {
|
||||
return $session;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief remove encryption related keys from the session
|
||||
*/
|
||||
public function closeEncryptionSession() {
|
||||
$session = new \OCA\Encryption\Session($this->view);
|
||||
$session->closeSession();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<fieldset class="personalblock">
|
||||
<h2><?php p( $l->t( 'Encryption' ) ); ?></h2>
|
||||
|
||||
<?php if ( ! $_["privateKeySet"] && $_["initialized"] ): ?>
|
||||
<?php if ( $_["initialized"] === '1' ): ?>
|
||||
<p>
|
||||
<a name="changePKPasswd" />
|
||||
<label for="changePrivateKeyPasswd">
|
||||
|
||||
@@ -155,7 +155,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testSymmetricStreamEncryptShortFileContent() {
|
||||
|
||||
$filename = 'tmp-' . time() . '.test';
|
||||
$filename = 'tmp-' . uniqid() . '.test';
|
||||
|
||||
$util = new Encryption\Util(new \OC_FilesystemView(), $this->userId);
|
||||
|
||||
@@ -214,7 +214,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
function testSymmetricStreamEncryptLongFileContent() {
|
||||
|
||||
// Generate a a random filename
|
||||
$filename = 'tmp-' . time() . '.test';
|
||||
$filename = 'tmp-' . uniqid() . '.test';
|
||||
|
||||
$util = new Encryption\Util(new \OC_FilesystemView(), $this->userId);
|
||||
|
||||
@@ -297,7 +297,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testSymmetricStreamDecryptShortFileContent() {
|
||||
|
||||
$filename = 'tmp-' . time();
|
||||
$filename = 'tmp-' . uniqid();
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///'. $this->userId . '/files/' . $filename, $this->dataShort);
|
||||
@@ -327,7 +327,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testSymmetricStreamDecryptLongFileContent() {
|
||||
|
||||
$filename = 'tmp-' . time();
|
||||
$filename = 'tmp-' . uniqid();
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong);
|
||||
@@ -418,7 +418,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testRenameFile() {
|
||||
|
||||
$filename = 'tmp-' . time();
|
||||
$filename = 'tmp-' . uniqid();
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong);
|
||||
@@ -431,7 +431,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->assertEquals($this->dataLong, $decrypt);
|
||||
|
||||
$newFilename = 'tmp-new-' . time();
|
||||
$newFilename = 'tmp-new-' . uniqid();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
$view->rename($filename, $newFilename);
|
||||
|
||||
@@ -449,7 +449,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testMoveFileIntoFolder() {
|
||||
|
||||
$filename = 'tmp-' . time();
|
||||
$filename = 'tmp-' . uniqid();
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong);
|
||||
@@ -462,8 +462,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->assertEquals($this->dataLong, $decrypt);
|
||||
|
||||
$newFolder = '/newfolder' . time();
|
||||
$newFilename = 'tmp-new-' . time();
|
||||
$newFolder = '/newfolder' . uniqid();
|
||||
$newFilename = 'tmp-new-' . uniqid();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
$view->mkdir($newFolder);
|
||||
$view->rename($filename, $newFolder . '/' . $newFilename);
|
||||
@@ -484,8 +484,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
$filename = '/tmp-' . time();
|
||||
$folder = '/folder' . time();
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$folder = '/folder' . uniqid();
|
||||
|
||||
$view->mkdir($folder);
|
||||
|
||||
@@ -500,7 +500,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->assertEquals($this->dataLong, $decrypt);
|
||||
|
||||
$newFolder = '/newfolder/subfolder' . time();
|
||||
$newFolder = '/newfolder/subfolder' . uniqid();
|
||||
$view->mkdir('/newfolder');
|
||||
|
||||
$view->rename($folder, $newFolder);
|
||||
@@ -519,7 +519,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
* @medium
|
||||
*/
|
||||
function testChangePassphrase() {
|
||||
$filename = 'tmp-' . time();
|
||||
$filename = 'tmp-' . uniqid();
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = file_put_contents('crypt:///' . $this->userId . '/files/' . $filename, $this->dataLong);
|
||||
@@ -557,7 +557,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testViewFilePutAndGetContents() {
|
||||
|
||||
$filename = '/tmp-' . time();
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -590,7 +590,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
* @large
|
||||
*/
|
||||
function testTouchExistingFile() {
|
||||
$filename = '/tmp-' . time();
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -614,7 +614,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
* @medium
|
||||
*/
|
||||
function testTouchFile() {
|
||||
$filename = '/tmp-' . time();
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
$view->touch($filename);
|
||||
@@ -638,7 +638,7 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
* @medium
|
||||
*/
|
||||
function testFopenFile() {
|
||||
$filename = '/tmp-' . time();
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
|
||||
require_once __DIR__ . '/../lib/helper.php';
|
||||
require_once __DIR__ . '/util.php';
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
@@ -16,6 +17,18 @@ use OCA\Encryption;
|
||||
*/
|
||||
class Test_Encryption_Helper extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_HELPER_USER1 = "test-helper-user1";
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER1, true);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
@@ -64,4 +77,28 @@ class Test_Encryption_Helper extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertEquals($relativePath, Encryption\Helper::getPathToRealFile($cachePath));
|
||||
}
|
||||
|
||||
function testGetUser() {
|
||||
|
||||
$path1 = "/" . self::TEST_ENCRYPTION_HELPER_USER1 . "/files/foo/bar.txt";
|
||||
$path2 = "/" . self::TEST_ENCRYPTION_HELPER_USER1 . "/cache/foo/bar.txt";
|
||||
$path3 = "/" . self::TEST_ENCRYPTION_HELPER_USER1 . "/thumbnails/foo";
|
||||
$path4 ="/" . "/" . self::TEST_ENCRYPTION_HELPER_USER1;
|
||||
|
||||
// if we are logged-in every path should return the currently logged-in user
|
||||
$this->assertEquals(self::TEST_ENCRYPTION_HELPER_USER1, Encryption\Helper::getUser($path3));
|
||||
|
||||
// now log out
|
||||
\Test_Encryption_Util::logoutHelper();
|
||||
|
||||
// now we should only get the user from /user/files and user/cache paths
|
||||
$this->assertEquals(self::TEST_ENCRYPTION_HELPER_USER1, Encryption\Helper::getUser($path1));
|
||||
$this->assertEquals(self::TEST_ENCRYPTION_HELPER_USER1, Encryption\Helper::getUser($path2));
|
||||
|
||||
$this->assertFalse(Encryption\Helper::getUser($path3));
|
||||
$this->assertFalse(Encryption\Helper::getUser($path4));
|
||||
|
||||
// Log-in again
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Helper::TEST_ENCRYPTION_HELPER_USER1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Bjoern Schiessle
|
||||
* @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../../lib/base.php';
|
||||
require_once __DIR__ . '/../lib/crypt.php';
|
||||
require_once __DIR__ . '/../lib/keymanager.php';
|
||||
require_once __DIR__ . '/../lib/stream.php';
|
||||
require_once __DIR__ . '/../lib/util.php';
|
||||
require_once __DIR__ . '/../appinfo/app.php';
|
||||
require_once __DIR__ . '/util.php';
|
||||
|
||||
use OCA\Encryption;
|
||||
|
||||
/**
|
||||
* Class Test_Encryption_Hooks
|
||||
* @brief this class provide basic hook app tests
|
||||
*/
|
||||
class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_HOOKS_USER1 = "test-encryption-hooks-user1";
|
||||
const TEST_ENCRYPTION_HOOKS_USER2 = "test-encryption-hooks-user2";
|
||||
|
||||
/**
|
||||
* @var \OC_FilesystemView
|
||||
*/
|
||||
public $user1View; // view on /data/user1/files
|
||||
public $user2View; // view on /data/user2/files
|
||||
public $rootView; // view on /data/user
|
||||
public $data;
|
||||
public $filename;
|
||||
public $folder;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
|
||||
\OC_Hook::clear('OC_Filesystem');
|
||||
\OC_Hook::clear('OC_User');
|
||||
|
||||
// clear share hooks
|
||||
\OC_Hook::clear('OCP\\Share');
|
||||
\OC::registerShareHooks();
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
|
||||
|
||||
// Filesystem related hooks
|
||||
\OCA\Encryption\Helper::registerFilesystemHooks();
|
||||
|
||||
// Sharing related hooks
|
||||
\OCA\Encryption\Helper::registerShareHooks();
|
||||
|
||||
// clear and register proxies
|
||||
\OC_FileProxy::clearProxies();
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1, true);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2, true);
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
// set user id
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
\OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
|
||||
// init filesystem view
|
||||
$this->user1View = new \OC_FilesystemView('/'. \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1 . '/files');
|
||||
$this->user2View = new \OC_FilesystemView('/'. \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '/files');
|
||||
$this->rootView = new \OC_FilesystemView('/');
|
||||
|
||||
// init short data
|
||||
$this->data = 'hats';
|
||||
$this->filename = 'enc_hooks_tests-' . uniqid() . '.txt';
|
||||
$this->folder = 'enc_hooks_tests_folder-' . uniqid();
|
||||
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
\OC_User::deleteUser(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
}
|
||||
|
||||
function testDeleteHooks() {
|
||||
|
||||
// remember files_trashbin state
|
||||
$stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
|
||||
|
||||
// we want to tests with app files_trashbin disabled
|
||||
\OC_App::disable('files_trashbin');
|
||||
|
||||
// make sure that the trash bin is disabled
|
||||
$this->assertFalse(\OC_APP::isEnabled('files_trashbin'));
|
||||
|
||||
$this->user1View->file_put_contents($this->filename, $this->data);
|
||||
|
||||
// check if all keys are generated
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->filename . '.key'));
|
||||
|
||||
|
||||
\Test_Encryption_Util::logoutHelper();
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
\OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
|
||||
|
||||
$this->user2View->file_put_contents($this->filename, $this->data);
|
||||
|
||||
// check if all keys are generated
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key'));
|
||||
|
||||
|
||||
// create a dummy file that we can delete something outside of data/user/files
|
||||
// in this case no share or file keys should be deleted
|
||||
$this->rootView->file_put_contents(self::TEST_ENCRYPTION_HOOKS_USER2 . "/" . $this->filename, $this->data);
|
||||
|
||||
// delete dummy file outside of data/user/files
|
||||
$this->rootView->unlink(self::TEST_ENCRYPTION_HOOKS_USER2 . "/" . $this->filename);
|
||||
|
||||
// all keys should still exist
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key'));
|
||||
|
||||
|
||||
// delete the file in data/user/files
|
||||
// now the correspondig share and file keys from user2 should be deleted
|
||||
$this->user2View->unlink($this->filename);
|
||||
|
||||
// check if keys from user2 are really deleted
|
||||
$this->assertFalse($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey'));
|
||||
$this->assertFalse($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key'));
|
||||
|
||||
// but user1 keys should still exist
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->filename . '.key'));
|
||||
|
||||
if ($stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
}
|
||||
else {
|
||||
OC_App::disable('files_trashbin');
|
||||
}
|
||||
}
|
||||
|
||||
function testDeleteHooksForSharedFiles() {
|
||||
|
||||
\Test_Encryption_Util::logoutHelper();
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
\OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
|
||||
// remember files_trashbin state
|
||||
$stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
|
||||
|
||||
// we want to tests with app files_trashbin disabled
|
||||
\OC_App::disable('files_trashbin');
|
||||
|
||||
// make sure that the trash bin is disabled
|
||||
$this->assertFalse(\OC_APP::isEnabled('files_trashbin'));
|
||||
|
||||
$this->user1View->file_put_contents($this->filename, $this->data);
|
||||
|
||||
// check if all keys are generated
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->filename . '.key'));
|
||||
|
||||
// get the file info from previous created file
|
||||
$fileInfo = $this->user1View->getFileInfo($this->filename);
|
||||
|
||||
// check if we have a valid file info
|
||||
$this->assertTrue(is_array($fileInfo));
|
||||
|
||||
// share the file with user2
|
||||
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_ENCRYPTION_HOOKS_USER2, OCP\PERMISSION_ALL);
|
||||
|
||||
// check if new share key exists
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey'));
|
||||
|
||||
\Test_Encryption_Util::logoutHelper();
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
\OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
|
||||
// user2 has a local file with the same name
|
||||
$this->user2View->file_put_contents($this->filename, $this->data);
|
||||
|
||||
// check if all keys are generated
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key'));
|
||||
|
||||
// delete the Shared file from user1 in data/user2/files/Shared
|
||||
$this->user2View->unlink('/Shared/' . $this->filename);
|
||||
|
||||
// now keys from user1s home should be gone
|
||||
$this->assertFalse($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
$this->assertFalse($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey'));
|
||||
$this->assertFalse($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->filename . '.key'));
|
||||
|
||||
// but user2 keys should still exist
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER2 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
self::TEST_ENCRYPTION_HOOKS_USER2 . '/files_encryption/keyfiles/' . $this->filename . '.key'));
|
||||
|
||||
// cleanup
|
||||
|
||||
$this->user2View->unlink($this->filename);
|
||||
|
||||
\Test_Encryption_Util::logoutHelper();
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
\OC_User::setUserId(\Test_Encryption_Hooks::TEST_ENCRYPTION_HOOKS_USER1);
|
||||
|
||||
// unshare the file
|
||||
\OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_ENCRYPTION_HOOKS_USER2);
|
||||
|
||||
$this->user1View->unlink($this->filename);
|
||||
|
||||
if ($stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
}
|
||||
else {
|
||||
OC_App::disable('files_trashbin');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief test rename operation
|
||||
*/
|
||||
function testRenameHook() {
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename, $this->data);
|
||||
|
||||
// test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
|
||||
// check if keys exists
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/'
|
||||
. $this->filename . '.key'));
|
||||
|
||||
// make subfolder and sub-subfolder
|
||||
$this->rootView->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder);
|
||||
$this->rootView->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder);
|
||||
|
||||
$this->assertTrue($this->rootView->is_dir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder));
|
||||
|
||||
// move the file out of the shared folder
|
||||
$root = $this->rootView->getRoot();
|
||||
$this->rootView->chroot('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/');
|
||||
$this->rootView->rename($this->filename, '/' . $this->folder . '/' . $this->folder . '/' . $this->filename);
|
||||
$this->rootView->chroot($root);
|
||||
|
||||
$this->assertFalse($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename));
|
||||
$this->assertTrue($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder . '/' . $this->filename));
|
||||
|
||||
// keys should be renamed too
|
||||
$this->assertFalse($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
$this->assertFalse($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/'
|
||||
. $this->filename . '.key'));
|
||||
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' . $this->folder . '/' . $this->folder . '/'
|
||||
. $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey'));
|
||||
$this->assertTrue($this->rootView->file_exists(
|
||||
'/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->folder . '/' . $this->folder . '/'
|
||||
. $this->filename . '.key'));
|
||||
|
||||
// cleanup
|
||||
$this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief replacing encryption keys during password change should be allowed
|
||||
* until the user logged in for the first time
|
||||
*/
|
||||
public function testSetPassphrase() {
|
||||
|
||||
$view = new \OC\Files\View();
|
||||
|
||||
// set user password for the first time
|
||||
\OCA\Encryption\Hooks::postCreateUser(array('uid' => 'newUser', 'password' => 'newUserPassword'));
|
||||
|
||||
$this->assertTrue($view->file_exists('public-keys/newUser.public.key'));
|
||||
$this->assertTrue($view->file_exists('newUser/files_encryption/newUser.private.key'));
|
||||
|
||||
// check if we are able to decrypt the private key
|
||||
$encryptedKey = \OCA\Encryption\Keymanager::getPrivateKey($view, 'newUser');
|
||||
$privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'newUserPassword');
|
||||
$this->assertTrue(is_string($privateKey));
|
||||
|
||||
// change the password before the user logged-in for the first time,
|
||||
// we can replace the encryption keys
|
||||
\OCA\Encryption\Hooks::setPassphrase(array('uid' => 'newUser', 'password' => 'passwordChanged'));
|
||||
|
||||
$encryptedKey = \OCA\Encryption\Keymanager::getPrivateKey($view, 'newUser');
|
||||
$privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged');
|
||||
$this->assertTrue(is_string($privateKey));
|
||||
|
||||
// now create a files folder to simulate a already used account
|
||||
$view->mkdir('/newUser/files');
|
||||
|
||||
// change the password after the user logged in, now the password should not change
|
||||
\OCA\Encryption\Hooks::setPassphrase(array('uid' => 'newUser', 'password' => 'passwordChanged2'));
|
||||
|
||||
$encryptedKey = \OCA\Encryption\Keymanager::getPrivateKey($view, 'newUser');
|
||||
$privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged2');
|
||||
$this->assertFalse($privateKey);
|
||||
|
||||
$privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged');
|
||||
$this->assertTrue(is_string($privateKey));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -136,6 +136,17 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertArrayHasKey('key', $sslInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @small
|
||||
*/
|
||||
function testGetFilenameFromShareKey() {
|
||||
$this->assertEquals("file",
|
||||
\TestProtectedKeymanagerMethods::testGetFilenameFromShareKey("file.user.shareKey"));
|
||||
$this->assertEquals("file.name.with.dots",
|
||||
\TestProtectedKeymanagerMethods::testGetFilenameFromShareKey("file.name.with.dots.user.shareKey"));
|
||||
$this->assertFalse(\TestProtectedKeymanagerMethods::testGetFilenameFromShareKey("file.txt"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
@@ -143,7 +154,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$key = $this->randomKey;
|
||||
|
||||
$file = 'unittest-' . time() . '.txt';
|
||||
$file = 'unittest-' . uniqid() . '.txt';
|
||||
|
||||
$util = new Encryption\Util($this->view, $this->userId);
|
||||
|
||||
@@ -196,7 +207,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
function testRecursiveDelShareKeys() {
|
||||
|
||||
// generate filename
|
||||
$filename = '/tmp-' . time() . '.txt';
|
||||
$filename = '/tmp-' . uniqid() . '.txt';
|
||||
|
||||
// create folder structure
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
@@ -234,3 +245,12 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dummy class to access protected methods of \OCA\Encryption\Keymanager for testing
|
||||
*/
|
||||
class TestProtectedKeymanagerMethods extends \OCA\Encryption\Keymanager {
|
||||
public static function testGetFilenameFromShareKey($sharekey) {
|
||||
return self::getFilenameFromShareKey($sharekey);
|
||||
}
|
||||
}
|
||||
@@ -44,8 +44,10 @@ class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @var \OC_FilesystemView
|
||||
*/
|
||||
public $view;
|
||||
public $view; // view in /data/user/files
|
||||
public $rootView; // view on /data/user
|
||||
public $data;
|
||||
public $filename;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
// reset backend
|
||||
@@ -74,9 +76,12 @@ class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// init filesystem view
|
||||
$this->view = new \OC_FilesystemView('/'. \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 . '/files');
|
||||
$this->rootView = new \OC_FilesystemView('/'. \Test_Encryption_Proxy::TEST_ENCRYPTION_PROXY_USER1 );
|
||||
|
||||
// init short data
|
||||
$this->data = 'hats';
|
||||
$this->filename = 'enc_proxy_tests-' . uniqid() . '.txt';
|
||||
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
@@ -90,21 +95,41 @@ class Test_Encryption_Proxy extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
function testPostFileSize() {
|
||||
|
||||
// generate filename
|
||||
$filename = 'tmp-' . time() . '.txt';
|
||||
|
||||
$this->view->file_put_contents($filename, $this->data);
|
||||
$this->view->file_put_contents($this->filename, $this->data);
|
||||
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$unencryptedSize = $this->view->filesize($filename);
|
||||
$unencryptedSize = $this->view->filesize($this->filename);
|
||||
|
||||
\OC_FileProxy::$enabled = true;
|
||||
|
||||
$encryptedSize = $this->view->filesize($filename);
|
||||
$encryptedSize = $this->view->filesize($this->filename);
|
||||
|
||||
$this->assertTrue($encryptedSize !== $unencryptedSize);
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink($this->filename);
|
||||
|
||||
}
|
||||
|
||||
function testPostFileSizeWithDirectory() {
|
||||
|
||||
$this->view->file_put_contents($this->filename, $this->data);
|
||||
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
// get root size, must match the file's unencrypted size
|
||||
$unencryptedSize = $this->view->filesize('');
|
||||
|
||||
\OC_FileProxy::$enabled = true;
|
||||
|
||||
$encryptedSize = $this->view->filesize('');
|
||||
|
||||
$this->assertTrue($encryptedSize !== $unencryptedSize);
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink($this->filename);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @medium
|
||||
* @param bool $withTeardown
|
||||
@@ -194,8 +195,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink(
|
||||
'/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename);
|
||||
$this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/');
|
||||
$this->view->unlink($this->filename);
|
||||
$this->view->chroot('/');
|
||||
|
||||
// check if share key not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -265,8 +267,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink(
|
||||
'/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename);
|
||||
$this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/');
|
||||
$this->view->unlink($this->filename);
|
||||
$this->view->chroot('/');
|
||||
|
||||
// check if share key not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -352,7 +355,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1);
|
||||
$this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files');
|
||||
$this->view->unlink($this->folder1);
|
||||
$this->view->chroot('/');
|
||||
|
||||
// check if share key not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -482,9 +487,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink(
|
||||
'/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1 . $this->subfolder
|
||||
. $this->subsubfolder . '/' . $this->filename);
|
||||
$this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files');
|
||||
$this->view->unlink($this->folder1 . $this->subfolder . $this->subsubfolder . '/' . $this->filename);
|
||||
$this->view->chroot('/');
|
||||
|
||||
// check if share key not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -494,6 +499,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function testPublicShareFile() {
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
@@ -559,7 +565,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . $publicShareKeyId . '.shareKey'));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename);
|
||||
$this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/');
|
||||
$this->view->unlink($this->filename);
|
||||
$this->view->chroot('/');
|
||||
|
||||
// check if share key not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -636,7 +644,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4 . '.shareKey'));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename);
|
||||
$this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/');
|
||||
$this->view->unlink($this->filename);
|
||||
$this->view->chroot('/');
|
||||
|
||||
// check if share key not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -649,9 +659,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
* @large
|
||||
*/
|
||||
function testRecoveryFile() {
|
||||
$this->markTestIncomplete(
|
||||
'No idea what\'s wrong here, this works perfectly in real-world. removeRecoveryKeys(\'/\') L709 removes correctly the keys, but for some reasons afterwards also the top-level folder "share-keys" is gone...'
|
||||
);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
@@ -733,8 +741,10 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . $recoveryKeyId . '.shareKey'));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename);
|
||||
$this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->folder1);
|
||||
$this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/');
|
||||
$this->view->unlink($this->filename);
|
||||
$this->view->unlink($this->folder1);
|
||||
$this->view->chroot('/');
|
||||
|
||||
// check if share key for recovery not exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -754,13 +764,13 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
* @large
|
||||
*/
|
||||
function testRecoveryForUser() {
|
||||
$this->markTestIncomplete(
|
||||
'This test drives Jenkins crazy - "Cannot modify header information - headers already sent" - line 811'
|
||||
);
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
\OCA\Encryption\Helper::adminEnableRecovery(null, 'test123');
|
||||
$result = \OCA\Encryption\Helper::adminEnableRecovery(null, 'test123');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$recoveryKeyId = OC_Appconfig::getValue('files_encryption', 'recoveryKeyId');
|
||||
|
||||
// login as user2
|
||||
@@ -771,6 +781,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
// enable recovery for admin
|
||||
$this->assertTrue($util->setRecoveryForUser(1));
|
||||
|
||||
// add recovery keys for existing files (e.g. the auto-generated welcome.txt)
|
||||
$util->addRecoveryKeys();
|
||||
|
||||
// create folder structure
|
||||
$this->view->mkdir('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files' . $this->folder1);
|
||||
$this->view->mkdir(
|
||||
@@ -809,6 +822,10 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// change password
|
||||
\OC_User::setPassword(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, 'test', 'test123');
|
||||
$params = array('uid' => \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2,
|
||||
'password' => 'test',
|
||||
'recoveryPassword' => 'test123');
|
||||
\OCA\Encryption\Hooks::setPassphrase($params);
|
||||
|
||||
// login as user2
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, false, 'test');
|
||||
@@ -823,8 +840,10 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
$this->assertEquals($this->dataShort, $retrievedCryptedFile2);
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files' . $this->folder1);
|
||||
$this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files' . $this->filename);
|
||||
$this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/');
|
||||
$this->view->unlink($this->folder1);
|
||||
$this->view->unlink($this->filename);
|
||||
$this->view->chroot('/');
|
||||
|
||||
// check if share key for user and recovery exists
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
@@ -842,11 +861,21 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->subfolder . $this->subsubfolder . '/'
|
||||
. $this->filename . '.' . $recoveryKeyId . '.shareKey'));
|
||||
|
||||
// enable recovery for admin
|
||||
|
||||
// disable recovery for admin
|
||||
$this->assertTrue($util->setRecoveryForUser(0));
|
||||
|
||||
\OCA\Encryption\Helper::adminDisableRecovery('test123');
|
||||
|
||||
$this->assertEquals(0, \OC_Appconfig::getValue('files_encryption', 'recoveryAdminEnabled'));
|
||||
|
||||
//clean up, reset passwords
|
||||
\OC_User::setPassword(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, 'test123');
|
||||
$params = array('uid' => \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2,
|
||||
'password' => \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2,
|
||||
'recoveryPassword' => 'test123');
|
||||
\OCA\Encryption\Hooks::setPassphrase($params);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -889,8 +918,8 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
} catch (Exception $e) {
|
||||
$this->assertEquals(0, strpos($e->getMessage(), "Following users are not set up for encryption"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
@@ -925,7 +954,70 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey'));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename);
|
||||
$this->view->chroot('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/');
|
||||
$this->view->unlink($this->filename);
|
||||
$this->view->chroot('/');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief test moving a shared file out of the Shared folder
|
||||
*/
|
||||
function testRename() {
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename, $this->dataShort);
|
||||
|
||||
// test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
|
||||
// get the file info from previous created file
|
||||
$fileInfo = $this->view->getFileInfo(
|
||||
'/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename);
|
||||
|
||||
// check if we have a valid file info
|
||||
$this->assertTrue(is_array($fileInfo));
|
||||
|
||||
// share the file
|
||||
\OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL);
|
||||
|
||||
// check if share key for user2exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files_encryption/share-keys/'
|
||||
. $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
|
||||
// login as user2
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2);
|
||||
|
||||
$this->assertTrue($this->view->file_exists('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename));
|
||||
|
||||
// get file contents
|
||||
$retrievedCryptedFile = $this->view->file_get_contents(
|
||||
'/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename);
|
||||
|
||||
// check if data is the same as we previously written
|
||||
$this->assertEquals($this->dataShort, $retrievedCryptedFile);
|
||||
|
||||
// move the file out of the shared folder
|
||||
$this->view->rename('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename,
|
||||
'/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename);
|
||||
|
||||
// check if we can read the moved file
|
||||
$retrievedRenamedFile = $this->view->file_get_contents(
|
||||
'/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename);
|
||||
|
||||
// check if data is the same as we previously written
|
||||
$this->assertEquals($this->dataShort, $retrievedRenamedFile);
|
||||
|
||||
// the owners file should be deleted
|
||||
$this->assertFalse($this->view->file_exists('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/' . $this->filename);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
function testStreamOptions() {
|
||||
$filename = '/tmp-' . time();
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -122,7 +122,7 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
function testStreamSetBlocking() {
|
||||
$filename = '/tmp-' . time();
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -136,6 +136,8 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
// set stream options
|
||||
$this->assertTrue(stream_set_blocking($handle, 1));
|
||||
|
||||
fclose($handle);
|
||||
|
||||
// tear down
|
||||
$view->unlink($filename);
|
||||
}
|
||||
@@ -144,7 +146,7 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
* @medium
|
||||
*/
|
||||
function testStreamSetTimeout() {
|
||||
$filename = '/tmp-' . time();
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -158,12 +160,14 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
// set stream options
|
||||
$this->assertFalse(stream_set_timeout($handle, 1));
|
||||
|
||||
fclose($handle);
|
||||
|
||||
// tear down
|
||||
$view->unlink($filename);
|
||||
}
|
||||
|
||||
function testStreamSetWriteBuffer() {
|
||||
$filename = '/tmp-' . time();
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$view = new \OC\Files\View('/' . $this->userId . '/files');
|
||||
|
||||
// Save short data as encrypted file using stream wrapper
|
||||
@@ -177,7 +181,48 @@ class Test_Encryption_Stream extends \PHPUnit_Framework_TestCase {
|
||||
// set stream options
|
||||
$this->assertEquals(0, stream_set_write_buffer($handle, 1024));
|
||||
|
||||
fclose($handle);
|
||||
|
||||
// tear down
|
||||
$view->unlink($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
* @brief test if stream wrapper can read files outside from the data folder
|
||||
*/
|
||||
function testStreamFromLocalFile() {
|
||||
|
||||
$filename = '/' . $this->userId . '/files/' . 'tmp-' . time().'.txt';
|
||||
|
||||
$tmpFilename = "/tmp/" . time() . ".txt";
|
||||
|
||||
// write an encrypted file
|
||||
$cryptedFile = $this->view->file_put_contents($filename, $this->dataShort);
|
||||
|
||||
// Test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
|
||||
// create a copy outside of the data folder in /tmp
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
$encryptedContent = $this->view->file_get_contents($filename);
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
file_put_contents($tmpFilename, $encryptedContent);
|
||||
|
||||
\OCA\Encryption\Helper::addTmpFileToMapper($tmpFilename, $filename);
|
||||
|
||||
// try to read the file from /tmp
|
||||
$handle = fopen("crypt://".$tmpFilename, "r");
|
||||
$contentFromTmpFile = stream_get_contents($handle);
|
||||
|
||||
// check if it was successful
|
||||
$this->assertEquals($this->dataShort, $contentFromTmpFile);
|
||||
|
||||
// clean up
|
||||
unlink($tmpFilename);
|
||||
$this->view->unlink($filename);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
function testDeleteFile() {
|
||||
|
||||
// generate filename
|
||||
$filename = 'tmp-' . time() . '.txt';
|
||||
$filename = 'tmp-' . uniqid() . '.txt';
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' .\Test_Encryption_Trashbin::TEST_ENCRYPTION_TRASHBIN_USER1. '/files/'. $filename, $this->dataShort);
|
||||
@@ -223,7 +223,7 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase {
|
||||
function testPermanentDeleteFile() {
|
||||
|
||||
// generate filename
|
||||
$filename = 'tmp-' . time() . '.txt';
|
||||
$filename = 'tmp-' . uniqid() . '.txt';
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///' .$this->userId. '/files/' . $filename, $this->dataShort);
|
||||
|
||||
@@ -64,6 +64,8 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
|
||||
function setUp() {
|
||||
// login user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
\OC_User::setUserId(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
$this->userId = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1;
|
||||
$this->pass = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1;
|
||||
@@ -132,6 +134,41 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
* @brief test detection of encrypted files
|
||||
*/
|
||||
function testIsEncryptedPath() {
|
||||
|
||||
$util = new Encryption\Util($this->view, $this->userId);
|
||||
|
||||
self::loginHelper($this->userId);
|
||||
|
||||
$unencryptedFile = '/tmpUnencrypted-' . time() . '.txt';
|
||||
$encryptedFile = '/tmpEncrypted-' . time() . '.txt';
|
||||
|
||||
// Disable encryption proxy to write a unencrypted file
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$this->view->file_put_contents($this->userId . '/files/' . $unencryptedFile, $this->dataShort);
|
||||
|
||||
// Re-enable proxy - our work is done
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
// write a encrypted file
|
||||
$this->view->file_put_contents($this->userId . '/files/' . $encryptedFile, $this->dataShort);
|
||||
|
||||
// test if both files are detected correctly
|
||||
$this->assertFalse($util->isEncryptedPath($this->userId . '/files/' . $unencryptedFile));
|
||||
$this->assertTrue($util->isEncryptedPath($this->userId . '/files/' . $encryptedFile));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink($this->userId . '/files/' . $unencryptedFile, $this->dataShort);
|
||||
$this->view->unlink($this->userId . '/files/' . $encryptedFile, $this->dataShort);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
* @brief test setup of encryption directories
|
||||
@@ -219,7 +256,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
\OC_User::setUserId(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
|
||||
$filename = '/tmp-' . time() . '.test';
|
||||
$filename = '/tmp-' . uniqid() . '.test';
|
||||
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
@@ -247,7 +284,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
function testGetFileSize() {
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
|
||||
$filename = 'tmp-' . time();
|
||||
$filename = 'tmp-' . uniqid();
|
||||
$externalFilename = '/' . $this->userId . '/files/' . $filename;
|
||||
|
||||
// Test for 0 byte files
|
||||
@@ -283,7 +320,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
function testEncryptAll() {
|
||||
|
||||
$filename = "/encryptAll" . time() . ".txt";
|
||||
$filename = "/encryptAll" . uniqid() . ".txt";
|
||||
$util = new Encryption\Util($this->view, $this->userId);
|
||||
|
||||
// disable encryption to upload a unencrypted file
|
||||
@@ -315,7 +352,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
function testDecryptAll() {
|
||||
|
||||
$filename = "/decryptAll" . time() . ".txt";
|
||||
$filename = "/decryptAll" . uniqid() . ".txt";
|
||||
$util = new Encryption\Util($this->view, $this->userId);
|
||||
|
||||
$this->view->file_put_contents($this->userId . '/files/' . $filename, $this->dataShort);
|
||||
@@ -323,9 +360,12 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
$fileInfoEncrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename);
|
||||
|
||||
$this->assertTrue(is_array($fileInfoEncrypted));
|
||||
$this->assertEquals($fileInfoEncrypted['encrypted'], 1);
|
||||
|
||||
// encrypt all unencrypted files
|
||||
$util->decryptAll('/' . $this->userId . '/' . 'files');
|
||||
// decrypt all encrypted files
|
||||
$result = $util->decryptAll('/' . $this->userId . '/' . 'files');
|
||||
|
||||
$this->assertTrue($result);
|
||||
|
||||
$fileInfoUnencrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename);
|
||||
|
||||
@@ -334,11 +374,83 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
// check if mtime and etags unchanged
|
||||
$this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']);
|
||||
$this->assertEquals($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']);
|
||||
// file should no longer be encrypted
|
||||
$this->assertEquals(0, $fileInfoUnencrypted['encrypted']);
|
||||
|
||||
$this->view->unlink($this->userId . '/files/' . $filename);
|
||||
|
||||
}
|
||||
|
||||
function testDescryptAllWithBrokenFiles() {
|
||||
|
||||
$file1 = "/decryptAll1" . uniqid() . ".txt";
|
||||
$file2 = "/decryptAll2" . uniqid() . ".txt";
|
||||
|
||||
$util = new Encryption\Util($this->view, $this->userId);
|
||||
|
||||
$this->view->file_put_contents($this->userId . '/files/' . $file1, $this->dataShort);
|
||||
$this->view->file_put_contents($this->userId . '/files/' . $file2, $this->dataShort);
|
||||
|
||||
$fileInfoEncrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1);
|
||||
$fileInfoEncrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2);
|
||||
|
||||
$this->assertTrue(is_array($fileInfoEncrypted1));
|
||||
$this->assertTrue(is_array($fileInfoEncrypted2));
|
||||
$this->assertEquals($fileInfoEncrypted1['encrypted'], 1);
|
||||
$this->assertEquals($fileInfoEncrypted2['encrypted'], 1);
|
||||
|
||||
// rename keyfile for file1 so that the decryption for file1 fails
|
||||
// Expected behaviour: decryptAll() returns false, file2 gets decrypted anyway
|
||||
$this->view->rename($this->userId . '/files_encryption/keyfiles/' . $file1 . '.key',
|
||||
$this->userId . '/files_encryption/keyfiles/' . $file1 . '.key.moved');
|
||||
|
||||
// decrypt all encrypted files
|
||||
$result = $util->decryptAll('/' . $this->userId . '/' . 'files');
|
||||
|
||||
$this->assertFalse($result);
|
||||
|
||||
$fileInfoUnencrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1);
|
||||
$fileInfoUnencrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2);
|
||||
|
||||
$this->assertTrue(is_array($fileInfoUnencrypted1));
|
||||
$this->assertTrue(is_array($fileInfoUnencrypted2));
|
||||
|
||||
// file1 should be still encrypted; file2 should be decrypted
|
||||
$this->assertEquals(1, $fileInfoUnencrypted1['encrypted']);
|
||||
$this->assertEquals(0, $fileInfoUnencrypted2['encrypted']);
|
||||
|
||||
// keyfiles and share keys should still exist
|
||||
$this->assertTrue($this->view->is_dir($this->userId . '/files_encryption/keyfiles/'));
|
||||
$this->assertTrue($this->view->is_dir($this->userId . '/files_encryption/share-keys/'));
|
||||
|
||||
// rename the keyfile for file1 back
|
||||
$this->view->rename($this->userId . '/files_encryption/keyfiles/' . $file1 . '.key.moved',
|
||||
$this->userId . '/files_encryption/keyfiles/' . $file1 . '.key');
|
||||
|
||||
// try again to decrypt all encrypted files
|
||||
$result = $util->decryptAll('/' . $this->userId . '/' . 'files');
|
||||
|
||||
$this->assertTrue($result);
|
||||
|
||||
$fileInfoUnencrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1);
|
||||
$fileInfoUnencrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2);
|
||||
|
||||
$this->assertTrue(is_array($fileInfoUnencrypted1));
|
||||
$this->assertTrue(is_array($fileInfoUnencrypted2));
|
||||
|
||||
// now both files should be decrypted
|
||||
$this->assertEquals(0, $fileInfoUnencrypted1['encrypted']);
|
||||
$this->assertEquals(0, $fileInfoUnencrypted2['encrypted']);
|
||||
|
||||
// keyfiles and share keys should be deleted
|
||||
$this->assertFalse($this->view->is_dir($this->userId . '/files_encryption/keyfiles/'));
|
||||
$this->assertFalse($this->view->is_dir($this->userId . '/files_encryption/share-keys/'));
|
||||
|
||||
$this->view->unlink($this->userId . '/files/' . $file1);
|
||||
$this->view->unlink($this->userId . '/files/' . $file2);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @large
|
||||
*/
|
||||
@@ -416,6 +528,12 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
OCA\Encryption\Hooks::login($params);
|
||||
}
|
||||
|
||||
public static function logoutHelper() {
|
||||
\OC_Util::tearDownFS();
|
||||
\OC_User::setUserId('');
|
||||
\OC\Files\Filesystem::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* helper function to set migration status to the right value
|
||||
* to be able to test the migration path
|
||||
|
||||
@@ -48,6 +48,8 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase {
|
||||
public $dataShort;
|
||||
public $stateFilesTrashbin;
|
||||
|
||||
private $storage;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
// reset backend
|
||||
\OC_User::clearBackends();
|
||||
@@ -77,8 +79,8 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase {
|
||||
$this->pass = \Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1;
|
||||
|
||||
// init filesystem view
|
||||
$this->view = new \OC_FilesystemView('/');
|
||||
|
||||
$this->view = new \OC\Files\View('/');
|
||||
list($this->storage, $intPath) = $this->view->resolvePath('/');
|
||||
// init short data
|
||||
$this->dataShort = 'hats';
|
||||
|
||||
@@ -113,7 +115,7 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase {
|
||||
function testWebdavPUT() {
|
||||
|
||||
// generate filename
|
||||
$filename = '/tmp-' . time() . '.txt';
|
||||
$filename = '/tmp-' . uniqid() . '.txt';
|
||||
|
||||
// set server vars
|
||||
$_SERVER['REQUEST_METHOD'] = 'OPTIONS';
|
||||
@@ -196,6 +198,9 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase {
|
||||
$_SERVER['HTTP_AUTHORIZATION'] = 'Basic dGVzdC13ZWJkYXYtdXNlcjE6dGVzdC13ZWJkYXYtdXNlcjE=';
|
||||
$_SERVER['PATH_INFO'] = '/webdav' . $filename;
|
||||
|
||||
// at the beginning the file should exist
|
||||
$this->assertTrue($this->view->file_exists('/' . $this->userId . '/files' . $filename));
|
||||
|
||||
// handle webdav request
|
||||
$content = $this->handleWebdavRequest();
|
||||
|
||||
|
||||
+11
-13
@@ -19,6 +19,11 @@
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# Addition 17/12/2012 Frank Karlitschek (frank@owncloud.org)
|
||||
# Addition 17/03/2014 Robin McCorkell (rmccorkell@karoshi.org.uk)
|
||||
# On the official website http://www.phpclasses.org/smb4php the
|
||||
# license is listed as LGPL so we assume that this is
|
||||
# dual-licensed GPL/LGPL
|
||||
###################################################################
|
||||
|
||||
define ('SMB4PHP_VERSION', '0.8');
|
||||
@@ -126,7 +131,7 @@ class smb {
|
||||
// this put env is necessary to read the output of smbclient correctly
|
||||
$old_locale = getenv('LC_ALL');
|
||||
putenv('LC_ALL=en_US.UTF-8');
|
||||
$output = popen (SMB4PHP_SMBCLIENT." -N {$auth} {$options} {$port} {$options} {$params} 2>/dev/null", 'r');
|
||||
$output = popen ('TZ=UTC '.SMB4PHP_SMBCLIENT." -N {$auth} {$options} {$port} {$options} {$params} 2>/dev/null", 'r');
|
||||
$gotInfo = false;
|
||||
$info = array ();
|
||||
$info['info']= array ();
|
||||
@@ -160,7 +165,7 @@ class smb {
|
||||
$i = ($mode == 'servers') ? array ($name, "server") : array ($name, "workgroup", $master);
|
||||
break;
|
||||
case 'files':
|
||||
list ($attr, $name) = preg_match ("/^(.*)[ ]+([D|A|H|S|R]+)$/", trim ($regs[1]), $regs2)
|
||||
list ($attr, $name) = preg_match ("/^(.*)[ ]+([D|A|H|S|R|N]+)$/", trim ($regs[1]), $regs2)
|
||||
? array (trim ($regs2[2]), trim ($regs2[1]))
|
||||
: array ('', trim ($regs[1]));
|
||||
list ($his, $im) = array (
|
||||
@@ -234,17 +239,10 @@ class smb {
|
||||
trigger_error ("url_stat(): list failed for host '{$pu['host']}'", E_USER_WARNING);
|
||||
break;
|
||||
case 'share':
|
||||
if ($o = smb::look ($pu)) {
|
||||
$found = FALSE;
|
||||
$lshare = strtolower ($pu['share']); # fix by Eric Leung
|
||||
foreach ($o['disk'] as $s) if ($lshare == strtolower($s)) {
|
||||
$found = TRUE;
|
||||
$stat = stat ("/tmp");
|
||||
break;
|
||||
}
|
||||
if (! $found)
|
||||
trigger_error ("url_stat(): disk resource '{$lshare}' not found in '{$pu['host']}'", E_USER_WARNING);
|
||||
}
|
||||
if (smb::execute("ls", $pu))
|
||||
$stat = stat ("/tmp");
|
||||
else
|
||||
trigger_error ("url_stat(): disk resource '{$pu['share']}' not found in '{$pu['host']}'", E_USER_WARNING);
|
||||
break;
|
||||
case 'path':
|
||||
if ($o = smb::execute ('dir "'.$pu['path'].'"', $pu)) {
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
<?php
|
||||
|
||||
OCP\JSON::checkAppEnabled('files_external');
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
if ($_POST['isPersonal'] == 'true') {
|
||||
OCP\JSON::checkLoggedIn();
|
||||
// Check whether the user has permissions to add personal storage backends
|
||||
if(OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') !== 'yes') {
|
||||
OCP\JSON::error(array('data' => array('message' => 'no permission')));
|
||||
return;
|
||||
}
|
||||
$isPersonal = true;
|
||||
} else {
|
||||
OCP\JSON::checkAdminUser();
|
||||
|
||||
@@ -24,5 +24,6 @@ if (OCP\Config::getAppValue('files_external', 'allow_user_mounting', 'yes') == '
|
||||
}
|
||||
|
||||
// connecting hooks
|
||||
OCP\Util::connectHook( 'OC_User', 'post_login', 'OC\Files\Storage\iRODS', 'login' );
|
||||
OCP\Util::connectHook('OC_Filesystem', 'post_initMountPoints', '\OC_Mount_Config', 'initMountPointsHook');
|
||||
OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\iRODS', 'login');
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ span.error {
|
||||
}
|
||||
|
||||
td.mountPoint, td.backend { width:10em; }
|
||||
td.remove>img { visibility:hidden; padding-top:0.8em; }
|
||||
td.remove>img { visibility:hidden; padding-top:0.5em; }
|
||||
tr:hover>td.remove>img { visibility:visible; cursor:pointer; }
|
||||
#addMountPoint>td { border:none; }
|
||||
#addMountPoint>td.applicable { visibility:hidden; }
|
||||
@@ -22,3 +22,8 @@ tr:hover>td.remove>img { visibility:visible; cursor:pointer; }
|
||||
#externalStorage label > input[type="checkbox"] {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
#externalStorage td.applicable div.chzn-container {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
|
||||
@@ -23,9 +23,12 @@ $(document).ready(function() {
|
||||
$(token).val(result.access_token);
|
||||
$(token_secret).val(result.access_token_secret);
|
||||
$(configured).val('true');
|
||||
OC.MountConfig.saveStorage(tr);
|
||||
$(tr).find('.configuration input').attr('disabled', 'disabled');
|
||||
$(tr).find('.configuration').append('<span id="access" style="padding-left:0.5em;">'+t('files_external', 'Access granted')+'</span>');
|
||||
OC.MountConfig.saveStorage(tr, function(status) {
|
||||
if (status) {
|
||||
$(tr).find('.configuration input').attr('disabled', 'disabled');
|
||||
$(tr).find('.configuration').append('<span id="access" style="padding-left:0.5em;">'+t('files_external', 'Access granted')+'</span>');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Dropbox storage'));
|
||||
}
|
||||
@@ -77,7 +80,6 @@ $(document).ready(function() {
|
||||
var tr = $(this).parent().parent();
|
||||
var app_key = $(this).parent().find('[data-parameter="app_key"]').val();
|
||||
var app_secret = $(this).parent().find('[data-parameter="app_secret"]').val();
|
||||
var statusSpan = $(tr).find('.status span');
|
||||
if (app_key != '' && app_secret != '') {
|
||||
var tr = $(this).parent().parent();
|
||||
var configured = $(this).parent().find('[data-parameter="configured"]');
|
||||
@@ -88,10 +90,9 @@ $(document).ready(function() {
|
||||
$(configured).val('false');
|
||||
$(token).val(result.data.request_token);
|
||||
$(token_secret).val(result.data.request_token_secret);
|
||||
OC.MountConfig.saveStorage(tr);
|
||||
statusSpan.removeClass();
|
||||
statusSpan.addClass('waiting');
|
||||
window.location = result.data.url;
|
||||
OC.MountConfig.saveStorage(tr, function() {
|
||||
window.location = result.data.url;
|
||||
});
|
||||
} else {
|
||||
OC.dialogs.alert(result.data.message, t('files_external', 'Error configuring Dropbox storage'));
|
||||
}
|
||||
|
||||
@@ -32,11 +32,14 @@ $(document).ready(function() {
|
||||
if (result && result.status == 'success') {
|
||||
$(token).val(result.data.token);
|
||||
$(configured).val('true');
|
||||
OC.MountConfig.saveStorage(tr);
|
||||
$(tr).find('.configuration input').attr('disabled', 'disabled');
|
||||
$(tr).find('.configuration').append($('<span/>')
|
||||
.attr('id', 'access')
|
||||
.text(t('files_external', 'Access granted')));
|
||||
OC.MountConfig.saveStorage(tr, function(status) {
|
||||
if (status) {
|
||||
$(tr).find('.configuration input').attr('disabled', 'disabled');
|
||||
$(tr).find('.configuration').append($('<span/>')
|
||||
.attr('id', 'access')
|
||||
.text(t('files_external', 'Access granted')));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
OC.dialogs.alert(result.data.message,
|
||||
t('files_external', 'Error configuring Google Drive storage')
|
||||
@@ -99,7 +102,6 @@ $(document).ready(function() {
|
||||
var configured = $(this).parent().find('[data-parameter="configured"]');
|
||||
var client_id = $(this).parent().find('[data-parameter="client_id"]').val();
|
||||
var client_secret = $(this).parent().find('[data-parameter="client_secret"]').val();
|
||||
var statusSpan = $(tr).find('.status span');
|
||||
if (client_id != '' && client_secret != '') {
|
||||
var token = $(this).parent().find('[data-parameter="token"]');
|
||||
$.post(OC.filePath('files_external', 'ajax', 'google.php'),
|
||||
@@ -112,10 +114,9 @@ $(document).ready(function() {
|
||||
if (result && result.status == 'success') {
|
||||
$(configured).val('false');
|
||||
$(token).val('false');
|
||||
OC.MountConfig.saveStorage(tr);
|
||||
statusSpan.removeClass();
|
||||
statusSpan.addClass('waiting');
|
||||
window.location = result.data.url;
|
||||
OC.MountConfig.saveStorage(tr, function(status) {
|
||||
window.location = result.data.url;
|
||||
});
|
||||
} else {
|
||||
OC.dialogs.alert(result.data.message,
|
||||
t('files_external', 'Error configuring Google Drive storage')
|
||||
|
||||
@@ -12,7 +12,7 @@ function updateStatus(statusEl, result){
|
||||
}
|
||||
|
||||
OC.MountConfig={
|
||||
saveStorage:function(tr) {
|
||||
saveStorage:function(tr, callback) {
|
||||
var mountPoint = $(tr).find('.mountPoint input').val();
|
||||
if (mountPoint == '') {
|
||||
return false;
|
||||
@@ -84,9 +84,15 @@ OC.MountConfig={
|
||||
},
|
||||
success: function(result) {
|
||||
status = updateStatus(statusSpan, result);
|
||||
if (callback) {
|
||||
callback(status);
|
||||
}
|
||||
},
|
||||
error: function(result){
|
||||
status = updateStatus(statusSpan, result);
|
||||
if (callback) {
|
||||
callback(status);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -137,9 +143,15 @@ OC.MountConfig={
|
||||
},
|
||||
success: function(result) {
|
||||
status = updateStatus(statusSpan, result);
|
||||
if (callback) {
|
||||
callback(status);
|
||||
}
|
||||
},
|
||||
error: function(result){
|
||||
status = updateStatus(statusSpan, result);
|
||||
if (callback) {
|
||||
callback(status);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?php
|
||||
$TRANSLATIONS = array(
|
||||
"Options" => "Опции",
|
||||
"Delete" => "Удалить"
|
||||
);
|
||||
$PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);";
|
||||
@@ -81,9 +81,9 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
$scheme = ($params['use_ssl'] === 'false') ? 'http' : 'https';
|
||||
$this->test = isset($params['test']);
|
||||
$this->timeout = ( ! isset($params['timeout'])) ? 15 : $params['timeout'];
|
||||
$params['region'] = ( ! isset($params['region'])) ? 'eu-west-1' : $params['region'];
|
||||
$params['hostname'] = ( !isset($params['hostname'])) ? 's3.amazonaws.com' : $params['hostname'];
|
||||
if (!isset($params['port'])) {
|
||||
$params['region'] = ( ! isset($params['region']) || $params['region'] === '' ) ? 'eu-west-1' : $params['region'];
|
||||
$params['hostname'] = ( !isset($params['hostname']) || $params['hostname'] === '' ) ? 's3.amazonaws.com' : $params['hostname'];
|
||||
if (!isset($params['port']) || $params['port'] === '') {
|
||||
$params['port'] = ($params['use_ssl'] === 'false') ? 80 : 443;
|
||||
}
|
||||
$base_url = $scheme.'://'.$params['hostname'].':'.$params['port'].'/';
|
||||
@@ -507,8 +507,10 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
}
|
||||
|
||||
public function test() {
|
||||
$test = $this->s3->get_canonical_user_id();
|
||||
if (isset($test['id']) && $test['id'] != '') {
|
||||
$test = $this->connection->getBucketAcl(array(
|
||||
'Bucket' => $this->bucket,
|
||||
));
|
||||
if (isset($test) && !is_null($test->getPath('Owner/ID'))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*
|
||||
* @author Michael Gapczynski
|
||||
* @copyright 2012 Michael Gapczynski mtgap@owncloud.com
|
||||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
@@ -19,15 +20,24 @@
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
set_include_path(
|
||||
get_include_path() . PATH_SEPARATOR .
|
||||
\OC_App::getAppPath('files_external') . '/3rdparty/phpseclib/phpseclib'
|
||||
);
|
||||
|
||||
/**
|
||||
* Class to configure the config/mount.php and data/$user/mount.php files
|
||||
*/
|
||||
* Class to configure mount.json globally and for users
|
||||
*/
|
||||
class OC_Mount_Config {
|
||||
// TODO: make this class non-static and give it a proper namespace
|
||||
|
||||
const MOUNT_TYPE_GLOBAL = 'global';
|
||||
const MOUNT_TYPE_GROUP = 'group';
|
||||
const MOUNT_TYPE_USER = 'user';
|
||||
|
||||
// whether to skip backend test (for unit tests, as this static class is not mockable)
|
||||
public static $skipTest = false;
|
||||
|
||||
/**
|
||||
* Get details on each of the external storage backends, used for the mount config UI
|
||||
* If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded
|
||||
@@ -39,6 +49,7 @@ class OC_Mount_Config {
|
||||
*/
|
||||
public static function getBackends() {
|
||||
|
||||
// FIXME: do not rely on php key order for the options order in the UI
|
||||
$backends['\OC\Files\Storage\Local']=array(
|
||||
'backend' => 'Local',
|
||||
'configuration' => array(
|
||||
@@ -50,9 +61,9 @@ class OC_Mount_Config {
|
||||
'key' => 'Access Key',
|
||||
'secret' => '*Secret Key',
|
||||
'bucket' => 'Bucket',
|
||||
'hostname' => 'Hostname (optional)',
|
||||
'port' => 'Port (optional)',
|
||||
'region' => 'Region (optional)',
|
||||
'hostname' => '&Hostname (optional)',
|
||||
'port' => '&Port (optional)',
|
||||
'region' => '&Region (optional)',
|
||||
'use_ssl' => '!Enable SSL',
|
||||
'use_path_style' => '!Enable Path Style'));
|
||||
|
||||
@@ -61,7 +72,7 @@ class OC_Mount_Config {
|
||||
'configuration' => array(
|
||||
'configured' => '#configured',
|
||||
'app_key' => 'App key',
|
||||
'app_secret' => 'App secret',
|
||||
'app_secret' => '*App secret',
|
||||
'token' => '#token',
|
||||
'token_secret' => '#token_secret'),
|
||||
'custom' => 'dropbox');
|
||||
@@ -69,7 +80,7 @@ class OC_Mount_Config {
|
||||
if(OC_Mount_Config::checkphpftp()) $backends['\OC\Files\Storage\FTP']=array(
|
||||
'backend' => 'FTP',
|
||||
'configuration' => array(
|
||||
'host' => 'URL',
|
||||
'host' => 'Hostname',
|
||||
'user' => 'Username',
|
||||
'password' => '*Password',
|
||||
'root' => '&Root',
|
||||
@@ -80,7 +91,7 @@ class OC_Mount_Config {
|
||||
'configuration' => array(
|
||||
'configured' => '#configured',
|
||||
'client_id' => 'Client ID',
|
||||
'client_secret' => 'Client secret',
|
||||
'client_secret' => '*Client secret',
|
||||
'token' => '#token'),
|
||||
'custom' => 'google');
|
||||
|
||||
@@ -145,6 +156,125 @@ class OC_Mount_Config {
|
||||
return($backends);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook that mounts the given user's visible mount points
|
||||
* @param array $data
|
||||
*/
|
||||
public static function initMountPointsHook($data) {
|
||||
$mountPoints = self::getAbsoluteMountPoints($data['user']);
|
||||
foreach ($mountPoints as $mountPoint => $options) {
|
||||
\OC\Files\Filesystem::mount($options['class'], $options['options'], $mountPoint);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mount points for the given user.
|
||||
* The mount point is relative to the data directory.
|
||||
*
|
||||
* @param string $user user
|
||||
* @return array of mount point string as key, mountpoint config as value
|
||||
*/
|
||||
public static function getAbsoluteMountPoints($user) {
|
||||
$mountPoints = array();
|
||||
|
||||
$datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data");
|
||||
$mount_file = \OC_Config::getValue("mount_file", $datadir . "/mount.json");
|
||||
|
||||
//move config file to it's new position
|
||||
if (is_file(\OC::$SERVERROOT . '/config/mount.json')) {
|
||||
rename(\OC::$SERVERROOT . '/config/mount.json', $mount_file);
|
||||
}
|
||||
|
||||
// Load system mount points
|
||||
$mountConfig = self::readData(false);
|
||||
if (isset($mountConfig[self::MOUNT_TYPE_GLOBAL])) {
|
||||
foreach ($mountConfig[self::MOUNT_TYPE_GLOBAL] as $mountPoint => $options) {
|
||||
$options['options'] = self::decryptPasswords($options['options']);
|
||||
$mountPoints[$mountPoint] = $options;
|
||||
}
|
||||
}
|
||||
if (isset($mountConfig[self::MOUNT_TYPE_GROUP])) {
|
||||
foreach ($mountConfig[self::MOUNT_TYPE_GROUP] as $group => $mounts) {
|
||||
if (\OC_Group::inGroup($user, $group)) {
|
||||
foreach ($mounts as $mountPoint => $options) {
|
||||
$mountPoint = self::setUserVars($user, $mountPoint);
|
||||
foreach ($options as &$option) {
|
||||
$option = self::setUserVars($user, $option);
|
||||
}
|
||||
$options['options'] = self::decryptPasswords($options['options']);
|
||||
$mountPoints[$mountPoint] = $options;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($mountConfig[self::MOUNT_TYPE_USER])) {
|
||||
foreach ($mountConfig[self::MOUNT_TYPE_USER] as $mountUser => $mounts) {
|
||||
if ($mountUser === 'all' or strtolower($mountUser) === strtolower($user)) {
|
||||
foreach ($mounts as $mountPoint => $options) {
|
||||
$mountPoint = self::setUserVars($user, $mountPoint);
|
||||
foreach ($options as &$option) {
|
||||
$option = self::setUserVars($user, $option);
|
||||
}
|
||||
$options['options'] = self::decryptPasswords($options['options']);
|
||||
$mountPoints[$mountPoint] = $options;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load personal mount points
|
||||
$mountConfig = self::readData(true);
|
||||
if (isset($mountConfig[self::MOUNT_TYPE_USER][$user])) {
|
||||
foreach ($mountConfig[self::MOUNT_TYPE_USER][$user] as $mountPoint => $options) {
|
||||
$options['options'] = self::decryptPasswords($options['options']);
|
||||
$mountPoints[$mountPoint] = $options;
|
||||
}
|
||||
}
|
||||
|
||||
return $mountPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* fill in the correct values for $user
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private static function setUserVars($user, $input) {
|
||||
return str_replace('$user', $user, $input);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get details on each of the external storage backends, used for the mount config UI
|
||||
* Some backends are not available as a personal backend, f.e. Local and such that have
|
||||
* been disabled by the admin.
|
||||
*
|
||||
* If a custom UI is needed, add the key 'custom' and a javascript file with that name will be loaded
|
||||
* If the configuration parameter should be secret, add a '*' to the beginning of the value
|
||||
* If the configuration parameter is a boolean, add a '!' to the beginning of the value
|
||||
* If the configuration parameter is optional, add a '&' to the beginning of the value
|
||||
* If the configuration parameter is hidden, add a '#' to the beginning of the value
|
||||
* @return array
|
||||
*/
|
||||
public static function getPersonalBackends() {
|
||||
|
||||
$backends = self::getBackends();
|
||||
|
||||
// Remove local storage and other disabled storages
|
||||
unset($backends['\OC\Files\Storage\Local']);
|
||||
|
||||
$allowed_backends = explode(',', OCP\Config::getAppValue('files_external', 'user_mounting_backends', ''));
|
||||
foreach ($backends as $backend => $null) {
|
||||
if (!in_array($backend, $allowed_backends)) {
|
||||
unset($backends[$backend]);
|
||||
}
|
||||
}
|
||||
|
||||
return $backends;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the system mount points
|
||||
* The returned array is not in the same format as getUserMountPoints()
|
||||
@@ -161,6 +291,7 @@ class OC_Mount_Config {
|
||||
if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
|
||||
$mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15);
|
||||
}
|
||||
$mount['options'] = self::decryptPasswords($mount['options']);
|
||||
// Remove '/$user/files/' from mount point
|
||||
$mountPoint = substr($mountPoint, 13);
|
||||
// Merge the mount point into the current mount points
|
||||
@@ -186,6 +317,7 @@ class OC_Mount_Config {
|
||||
if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
|
||||
$mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15);
|
||||
}
|
||||
$mount['options'] = self::decryptPasswords($mount['options']);
|
||||
// Remove '/$user/files/' from mount point
|
||||
$mountPoint = substr($mountPoint, 13);
|
||||
// Merge the mount point into the current mount points
|
||||
@@ -223,6 +355,7 @@ class OC_Mount_Config {
|
||||
if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
|
||||
$mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15);
|
||||
}
|
||||
$mount['options'] = self::decryptPasswords($mount['options']);
|
||||
// Remove '/uid/files/' from mount point
|
||||
$personal[substr($mountPoint, strlen($uid) + 8)] = array(
|
||||
'class' => $mount['class'],
|
||||
@@ -235,15 +368,25 @@ class OC_Mount_Config {
|
||||
return $personal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test connecting using the given backend configuration
|
||||
* @param string $class backend class name
|
||||
* @param array $options backend configuration options
|
||||
* @return bool true if the connection succeeded, false otherwise
|
||||
*/
|
||||
private static function getBackendStatus($class, $options) {
|
||||
if (self::$skipTest) {
|
||||
return true;
|
||||
}
|
||||
foreach ($options as &$option) {
|
||||
$option = str_replace('$user', OCP\User::getUser(), $option);
|
||||
$option = self::setUserVars(OCP\User::getUser(), $option);
|
||||
}
|
||||
if (class_exists($class)) {
|
||||
try {
|
||||
$storage = new $class($options);
|
||||
return $storage->test();
|
||||
} catch (Exception $exception) {
|
||||
\OCP\Util::logException('files_external', $exception);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -266,22 +409,34 @@ class OC_Mount_Config {
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal = false) {
|
||||
$backends = self::getBackends();
|
||||
$mountPoint = OC\Files\Filesystem::normalizePath($mountPoint);
|
||||
if ($mountPoint === '' || $mountPoint === '/' || $mountPoint == '/Shared') {
|
||||
// can't mount at root or "Shared" folder
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($backends[$class])) {
|
||||
// invalid backend
|
||||
return false;
|
||||
}
|
||||
if ($isPersonal) {
|
||||
// Verify that the mount point applies for the current user
|
||||
// Prevent non-admin users from mounting local storage
|
||||
if ($applicable != OCP\User::getUser() || $class == '\OC\Files\Storage\Local') {
|
||||
if ($applicable !== OCP\User::getUser() || strtolower($class) === '\oc\files\storage\local') {
|
||||
return false;
|
||||
}
|
||||
$mountPoint = '/'.$applicable.'/files/'.ltrim($mountPoint, '/');
|
||||
} else {
|
||||
$mountPoint = '/$user/files/'.ltrim($mountPoint, '/');
|
||||
}
|
||||
$mount = array($applicable => array($mountPoint => array('class' => $class, 'options' => $classOptions)));
|
||||
|
||||
$mount = array($applicable => array(
|
||||
$mountPoint => array(
|
||||
'class' => $class,
|
||||
'options' => self::encryptPasswords($classOptions))
|
||||
)
|
||||
);
|
||||
$mountPoints = self::readData($isPersonal);
|
||||
// Merge the new mount point into the current mount points
|
||||
if (isset($mountPoints[$mountType])) {
|
||||
@@ -381,8 +536,7 @@ class OC_Mount_Config {
|
||||
* @return array
|
||||
*/
|
||||
public static function getCertificates() {
|
||||
$view = \OCP\Files::getStorage('files_external');
|
||||
$path=\OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath("").'uploads/';
|
||||
$path=OC_User::getHome(OC_User::getUser()) . '/files_external/uploads/';
|
||||
\OCP\Util::writeLog('files_external', 'checking path '.$path, \OCP\Util::INFO);
|
||||
if ( ! is_dir($path)) {
|
||||
//path might not exist (e.g. non-standard OC_User::getHome() value)
|
||||
@@ -404,8 +558,7 @@ class OC_Mount_Config {
|
||||
* creates certificate bundle
|
||||
*/
|
||||
public static function createCertificateBundle() {
|
||||
$view = \OCP\Files::getStorage("files_external");
|
||||
$path = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath("");
|
||||
$path=OC_User::getHome(OC_User::getUser()) . '/files_external';
|
||||
|
||||
$certs = OC_Mount_Config::getCertificates();
|
||||
$fh_certs = fopen($path."/rootcerts.crt", 'w');
|
||||
@@ -475,4 +628,71 @@ class OC_Mount_Config {
|
||||
|
||||
return $txt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt passwords in the given config options
|
||||
* @param array $options mount options
|
||||
* @return array updated options
|
||||
*/
|
||||
private static function encryptPasswords($options) {
|
||||
if (isset($options['password'])) {
|
||||
$options['password_encrypted'] = self::encryptPassword($options['password']);
|
||||
// do not unset the password, we want to keep the keys order
|
||||
// on load... because that's how the UI currently works
|
||||
$options['password'] = '';
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt passwords in the given config options
|
||||
* @param array $options mount options
|
||||
* @return array updated options
|
||||
*/
|
||||
private static function decryptPasswords($options) {
|
||||
// note: legacy options might still have the unencrypted password in the "password" field
|
||||
if (isset($options['password_encrypted'])) {
|
||||
$options['password'] = self::decryptPassword($options['password_encrypted']);
|
||||
unset($options['password_encrypted']);
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a single password
|
||||
* @param string $password plain text password
|
||||
* @return encrypted password
|
||||
*/
|
||||
private static function encryptPassword($password) {
|
||||
$cipher = self::getCipher();
|
||||
$iv = \OCP\Util::generateRandomBytes(16);
|
||||
$cipher->setIV($iv);
|
||||
return base64_encode($iv . $cipher->encrypt($password));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts a single password
|
||||
* @param string $encryptedPassword encrypted password
|
||||
* @return plain text password
|
||||
*/
|
||||
private static function decryptPassword($encryptedPassword) {
|
||||
$cipher = self::getCipher();
|
||||
$binaryPassword = base64_decode($encryptedPassword);
|
||||
$iv = substr($binaryPassword, 0, 16);
|
||||
$cipher->setIV($iv);
|
||||
$binaryPassword = substr($binaryPassword, 16);
|
||||
return $cipher->decrypt($binaryPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encryption cipher
|
||||
*/
|
||||
private static function getCipher() {
|
||||
if (!class_exists('Crypt_AES', false)) {
|
||||
include('Crypt/AES.php');
|
||||
}
|
||||
$cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
|
||||
$cipher->setKey(\OCP\Config::getSystemValue('passwordsalt'));
|
||||
return $cipher;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class SMB extends \OC\Files\Storage\StreamWrapper{
|
||||
$this->share = substr($this->share, 0, -1);
|
||||
}
|
||||
} else {
|
||||
throw new \Exception();
|
||||
throw new \Exception('Invalid configuration');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -245,6 +245,10 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
$mtime = $object->extra_headers['X-Object-Meta-Timestamp'];
|
||||
}
|
||||
|
||||
if (!empty($mtime)) {
|
||||
$mtime = floor($mtime);
|
||||
}
|
||||
|
||||
$stat = array();
|
||||
$stat['size'] = $object->content_length;
|
||||
$stat['mtime'] = $mtime;
|
||||
@@ -364,7 +368,7 @@ class Swift extends \OC\Files\Storage\Common {
|
||||
'X-Object-Meta-Timestamp' => $mtime
|
||||
)
|
||||
);
|
||||
return $object->Update($settings);
|
||||
return $object->UpdateMetadata($settings);
|
||||
} else {
|
||||
$object = $this->container->DataObject();
|
||||
if (is_null($mtime)) {
|
||||
|
||||
@@ -14,6 +14,7 @@ class DAV extends \OC\Files\Storage\Common{
|
||||
private $host;
|
||||
private $secure;
|
||||
private $root;
|
||||
private $certPath;
|
||||
private $ready;
|
||||
/**
|
||||
* @var \Sabre_DAV_Client
|
||||
@@ -40,6 +41,12 @@ class DAV extends \OC\Files\Storage\Common{
|
||||
} else {
|
||||
$this->secure = false;
|
||||
}
|
||||
if ($this->secure === true) {
|
||||
$certPath=\OC_User::getHome(\OC_User::getUser()) . '/files_external/rootcerts.crt';
|
||||
if (file_exists($certPath)) {
|
||||
$this->certPath=$certPath;
|
||||
}
|
||||
}
|
||||
$this->root=isset($params['root'])?$params['root']:'/';
|
||||
if ( ! $this->root || $this->root[0]!='/') {
|
||||
$this->root='/'.$this->root;
|
||||
@@ -58,20 +65,16 @@ class DAV extends \OC\Files\Storage\Common{
|
||||
}
|
||||
$this->ready = true;
|
||||
|
||||
$settings = array(
|
||||
'baseUri' => $this->createBaseUri(),
|
||||
'userName' => $this->user,
|
||||
'password' => $this->password,
|
||||
);
|
||||
$settings = array(
|
||||
'baseUri' => $this->createBaseUri(),
|
||||
'userName' => $this->user,
|
||||
'password' => $this->password,
|
||||
);
|
||||
|
||||
$this->client = new \Sabre_DAV_Client($settings);
|
||||
|
||||
$caview = \OCP\Files::getStorage('files_external');
|
||||
if ($caview) {
|
||||
$certPath=\OCP\Config::getSystemValue('datadirectory').$caview->getAbsolutePath("").'rootcerts.crt';
|
||||
if (file_exists($certPath)) {
|
||||
$this->client->addTrustedCertificates($certPath);
|
||||
}
|
||||
if ($this->secure === true && $this->certPath) {
|
||||
$this->client->addTrustedCertificates($this->certPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +169,14 @@ class DAV extends \OC\Files\Storage\Common{
|
||||
curl_setopt($curl, CURLOPT_URL, $this->createBaseUri().str_replace(' ', '%20', $path));
|
||||
curl_setopt($curl, CURLOPT_FILE, $fp);
|
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
if ($this->secure === true) {
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
if($this->certPath){
|
||||
curl_setopt($curl, CURLOPT_CAINFO, $this->certPath);
|
||||
}
|
||||
}
|
||||
|
||||
curl_exec ($curl);
|
||||
curl_close ($curl);
|
||||
rewind($fp);
|
||||
@@ -214,7 +224,7 @@ class DAV extends \OC\Files\Storage\Common{
|
||||
if (isset($response['{DAV:}quota-available-bytes'])) {
|
||||
return (int)$response['{DAV:}quota-available-bytes'];
|
||||
} else {
|
||||
return 0;
|
||||
return \OC\Files\SPACE_UNKNOWN;
|
||||
}
|
||||
} catch(\Exception $e) {
|
||||
return \OC\Files\SPACE_UNKNOWN;
|
||||
@@ -254,6 +264,13 @@ class DAV extends \OC\Files\Storage\Common{
|
||||
curl_setopt($curl, CURLOPT_INFILE, $source); // file pointer
|
||||
curl_setopt($curl, CURLOPT_INFILESIZE, filesize($path));
|
||||
curl_setopt($curl, CURLOPT_PUT, true);
|
||||
if ($this->secure === true) {
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
if($this->certPath){
|
||||
curl_setopt($curl, CURLOPT_CAINFO, $this->certPath);
|
||||
}
|
||||
}
|
||||
curl_exec ($curl);
|
||||
curl_close ($curl);
|
||||
}
|
||||
@@ -331,3 +348,4 @@ class DAV extends \OC\Files\Storage\Common{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
class="optional"
|
||||
data-parameter="<?php p($parameter); ?>"
|
||||
value="<?php p($value); ?>"
|
||||
placeholder="<?php p(substr($placeholder, 5)); ?>" />
|
||||
placeholder="<?php p(substr($placeholder, 1)); ?>" />
|
||||
<?php elseif (strpos($placeholder, '#') !== false): ?>
|
||||
<input type="hidden"
|
||||
data-parameter="<?php p($parameter); ?>"
|
||||
|
||||
@@ -34,6 +34,90 @@ class Test_Mount_Config_Dummy_Storage {
|
||||
* Class Test_Mount_Config
|
||||
*/
|
||||
class Test_Mount_Config extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
private $dataDir;
|
||||
private $userHome;
|
||||
private $oldAllowedBackends;
|
||||
private $allBackends;
|
||||
|
||||
const TEST_USER1 = 'user1';
|
||||
const TEST_USER2 = 'user2';
|
||||
const TEST_GROUP1 = 'group1';
|
||||
const TEST_GROUP2 = 'group2';
|
||||
|
||||
public function setUp() {
|
||||
\OC_User::createUser(self::TEST_USER1, self::TEST_USER1);
|
||||
\OC_User::createUser(self::TEST_USER2, self::TEST_USER2);
|
||||
|
||||
\OC_Group::createGroup(self::TEST_GROUP1);
|
||||
\OC_Group::addToGroup(self::TEST_USER1, self::TEST_GROUP1);
|
||||
\OC_Group::createGroup(self::TEST_GROUP2);
|
||||
\OC_Group::addToGroup(self::TEST_USER2, self::TEST_GROUP2);
|
||||
|
||||
\OC_User::setUserId(self::TEST_USER1);
|
||||
$this->userHome = \OC_User::getHome(self::TEST_USER1);
|
||||
mkdir($this->userHome);
|
||||
|
||||
$this->dataDir = \OC_Config::getValue(
|
||||
'datadirectory',
|
||||
\OC::$SERVERROOT . '/data/'
|
||||
);
|
||||
$this->oldAllowedBackends = OCP\Config::getAppValue(
|
||||
'files_external',
|
||||
'user_mounting_backends',
|
||||
''
|
||||
);
|
||||
$this->allBackends = OC_Mount_Config::getBackends();
|
||||
OCP\Config::setAppValue(
|
||||
'files_external',
|
||||
'user_mounting_backends',
|
||||
implode(',', array_keys($this->allBackends))
|
||||
);
|
||||
|
||||
OC_Mount_Config::$skipTest = true;
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
OC_Mount_Config::$skipTest = false;
|
||||
|
||||
\OC_User::deleteUser(self::TEST_USER2);
|
||||
\OC_User::deleteUser(self::TEST_USER1);
|
||||
\OC_Group::deleteGroup(self::TEST_GROUP1);
|
||||
\OC_Group::deleteGroup(self::TEST_GROUP2);
|
||||
|
||||
@unlink($this->dataDir . '/mount.json');
|
||||
|
||||
OCP\Config::setAppValue(
|
||||
'files_external',
|
||||
'user_mounting_backends',
|
||||
$this->oldAllowedBackends
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the global config, for checking
|
||||
*/
|
||||
private function readGlobalConfig() {
|
||||
$configFile = $this->dataDir . '/mount.json';
|
||||
return json_decode(file_get_contents($configFile), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the user config, for checking
|
||||
*/
|
||||
private function readUserConfig() {
|
||||
$configFile = $this->userHome . '/mount.json';
|
||||
return json_decode(file_get_contents($configFile), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the user config, to simulate existing files
|
||||
*/
|
||||
private function writeUserConfig($config) {
|
||||
$configFile = $this->userHome . '/mount.json';
|
||||
file_put_contents($configFile, json_encode($config));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test mount point validation
|
||||
*/
|
||||
@@ -42,10 +126,329 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase {
|
||||
$mountType = 'user';
|
||||
$applicable = 'all';
|
||||
$isPersonal = false;
|
||||
$this->assertEquals(false, OC_Mount_Config::addMountPoint('', $storageClass, array(), $mountType, $applicable, $isPersonal));
|
||||
$this->assertEquals(false, OC_Mount_Config::addMountPoint('/', $storageClass, array(), $mountType, $applicable, $isPersonal));
|
||||
$this->assertEquals(false, OC_Mount_Config::addMountPoint('Shared', $storageClass, array(), $mountType, $applicable, $isPersonal));
|
||||
$this->assertEquals(false, OC_Mount_Config::addMountPoint('/Shared', $storageClass, array(), $mountType, $applicable, $isPersonal));
|
||||
$this->assertFalse(OC_Mount_Config::addMountPoint('', $storageClass, array(), $mountType, $applicable, $isPersonal));
|
||||
$this->assertFalse(OC_Mount_Config::addMountPoint('/', $storageClass, array(), $mountType, $applicable, $isPersonal));
|
||||
$this->assertFalse(OC_Mount_Config::addMountPoint('Shared', $storageClass, array(), $mountType, $applicable, $isPersonal));
|
||||
$this->assertFalse(OC_Mount_Config::addMountPoint('/Shared', $storageClass, array(), $mountType, $applicable, $isPersonal));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding a global mount point
|
||||
*/
|
||||
public function testAddGlobalMountPoint() {
|
||||
$mountType = OC_Mount_Config::MOUNT_TYPE_USER;
|
||||
$applicable = 'all';
|
||||
$isPersonal = false;
|
||||
|
||||
$this->assertEquals(true, OC_Mount_Config::addMountPoint('/ext', '\OC\Files\Storage\SFTP', array(), $mountType, $applicable, $isPersonal));
|
||||
|
||||
$config = $this->readGlobalConfig();
|
||||
$this->assertEquals(1, count($config));
|
||||
$this->assertTrue(isset($config[$mountType]));
|
||||
$this->assertTrue(isset($config[$mountType][$applicable]));
|
||||
$this->assertTrue(isset($config[$mountType][$applicable]['/$user/files/ext']));
|
||||
$this->assertEquals(
|
||||
'\OC\Files\Storage\SFTP',
|
||||
$config[$mountType][$applicable]['/$user/files/ext']['class']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding a personal mount point
|
||||
*/
|
||||
public function testAddMountPointSingleUser() {
|
||||
$mountType = OC_Mount_Config::MOUNT_TYPE_USER;
|
||||
$applicable = self::TEST_USER1;
|
||||
$isPersonal = true;
|
||||
|
||||
$this->assertEquals(true, OC_Mount_Config::addMountPoint('/ext', '\OC\Files\Storage\SFTP', array(), $mountType, $applicable, $isPersonal));
|
||||
|
||||
$config = $this->readUserConfig();
|
||||
$this->assertEquals(1, count($config));
|
||||
$this->assertTrue(isset($config[$mountType]));
|
||||
$this->assertTrue(isset($config[$mountType][$applicable]));
|
||||
$this->assertTrue(isset($config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext']));
|
||||
$this->assertEquals(
|
||||
'\OC\Files\Storage\SFTP',
|
||||
$config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext']['class']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding a mount point with an non-existant backend
|
||||
*/
|
||||
public function testAddMountPointUnexistClass() {
|
||||
$storageClass = 'Unexist_Storage';
|
||||
$mountType = OC_Mount_Config::MOUNT_TYPE_USER;
|
||||
$applicable = self::TEST_USER1;
|
||||
$isPersonal = false;
|
||||
$this->assertFalse(OC_Mount_Config::addMountPoint('/ext', $storageClass, array(), $mountType, $applicable, $isPersonal));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reading and writing global config
|
||||
*/
|
||||
public function testReadWriteGlobalConfig() {
|
||||
$mountType = OC_Mount_Config::MOUNT_TYPE_USER;
|
||||
$applicable = 'all';
|
||||
$isPersonal = false;
|
||||
$mountConfig = array(
|
||||
'host' => 'smbhost',
|
||||
'user' => 'smbuser',
|
||||
'password' => 'smbpassword',
|
||||
'share' => 'smbshare',
|
||||
'root' => 'smbroot'
|
||||
);
|
||||
|
||||
// write config
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$mountConfig,
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
// re-read config
|
||||
$config = OC_Mount_Config::getSystemMountPoints();
|
||||
$this->assertEquals(1, count($config));
|
||||
$this->assertTrue(isset($config['ext']));
|
||||
$this->assertEquals('\OC\Files\Storage\SMB', $config['ext']['class']);
|
||||
$savedMountConfig = $config['ext']['configuration'];
|
||||
$this->assertEquals($mountConfig, $savedMountConfig);
|
||||
// key order needs to be preserved for the UI...
|
||||
$this->assertEquals(array_keys($mountConfig), array_keys($savedMountConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reading and writing config
|
||||
*/
|
||||
public function testReadWritePersonalConfig() {
|
||||
$mountType = OC_Mount_Config::MOUNT_TYPE_USER;
|
||||
$applicable = self::TEST_USER1;
|
||||
$isPersonal = true;
|
||||
$mountConfig = array(
|
||||
'host' => 'smbhost',
|
||||
'user' => 'smbuser',
|
||||
'password' => 'smbpassword',
|
||||
'share' => 'smbshare',
|
||||
'root' => 'smbroot'
|
||||
);
|
||||
|
||||
// write config
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$mountConfig,
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
// re-read config
|
||||
$config = OC_Mount_Config::getPersonalMountPoints();
|
||||
$this->assertEquals(1, count($config));
|
||||
$this->assertTrue(isset($config['ext']));
|
||||
$this->assertEquals('\OC\Files\Storage\SMB', $config['ext']['class']);
|
||||
$savedMountConfig = $config['ext']['configuration'];
|
||||
$this->assertEquals($mountConfig, $savedMountConfig);
|
||||
// key order needs to be preserved for the UI...
|
||||
$this->assertEquals(array_keys($mountConfig), array_keys($savedMountConfig));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test password obfuscation
|
||||
*/
|
||||
public function testPasswordObfuscation() {
|
||||
$mountType = OC_Mount_Config::MOUNT_TYPE_USER;
|
||||
$applicable = self::TEST_USER1;
|
||||
$isPersonal = true;
|
||||
$mountConfig = array(
|
||||
'host' => 'smbhost',
|
||||
'user' => 'smbuser',
|
||||
'password' => 'smbpassword',
|
||||
'share' => 'smbshare',
|
||||
'root' => 'smbroot'
|
||||
);
|
||||
|
||||
// write config
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$mountConfig,
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
// note: password re-reading is covered by testReadWritePersonalConfig
|
||||
|
||||
// check that password inside the file is NOT in plain text
|
||||
$config = $this->readUserConfig();
|
||||
$savedConfig = $config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext']['options'];
|
||||
|
||||
// no more clear text password in file (kept because of key order)
|
||||
$this->assertEquals('', $savedConfig['password']);
|
||||
|
||||
// encrypted password is present
|
||||
$this->assertNotEquals($mountConfig['password'], $savedConfig['password_encrypted']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test read legacy passwords
|
||||
*/
|
||||
public function testReadLegacyPassword() {
|
||||
$mountType = OC_Mount_Config::MOUNT_TYPE_USER;
|
||||
$applicable = self::TEST_USER1;
|
||||
$isPersonal = true;
|
||||
$mountConfig = array(
|
||||
'host' => 'smbhost',
|
||||
'user' => 'smbuser',
|
||||
'password' => 'smbpassword',
|
||||
'share' => 'smbshare',
|
||||
'root' => 'smbroot'
|
||||
);
|
||||
|
||||
// write config
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$mountConfig,
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
$config = $this->readUserConfig();
|
||||
// simulate non-encrypted password situation
|
||||
$config[$mountType][$applicable]['/' . self::TEST_USER1 . '/files/ext']['options']['password'] = 'smbpasswd';
|
||||
|
||||
$this->writeUserConfig($config);
|
||||
|
||||
// re-read config, password was read correctly
|
||||
$config = OC_Mount_Config::getPersonalMountPoints();
|
||||
$savedMountConfig = $config['ext']['configuration'];
|
||||
$this->assertEquals($mountConfig, $savedMountConfig);
|
||||
}
|
||||
|
||||
public function mountDataProvider() {
|
||||
return array(
|
||||
// Tests for visible mount points
|
||||
// system mount point for all users
|
||||
array(
|
||||
false,
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
'all',
|
||||
self::TEST_USER1,
|
||||
true,
|
||||
),
|
||||
// system mount point for a specific user
|
||||
array(
|
||||
false,
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
self::TEST_USER1,
|
||||
self::TEST_USER1,
|
||||
true,
|
||||
),
|
||||
// system mount point for a specific group
|
||||
array(
|
||||
false,
|
||||
OC_Mount_Config::MOUNT_TYPE_GROUP,
|
||||
self::TEST_GROUP1,
|
||||
self::TEST_USER1,
|
||||
true,
|
||||
),
|
||||
// user mount point
|
||||
array(
|
||||
true,
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
self::TEST_USER1,
|
||||
self::TEST_USER1,
|
||||
true,
|
||||
),
|
||||
|
||||
// Tests for non-visible mount points
|
||||
// system mount point for another user
|
||||
array(
|
||||
false,
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
self::TEST_USER2,
|
||||
self::TEST_USER1,
|
||||
false,
|
||||
),
|
||||
// system mount point for a specific group
|
||||
array(
|
||||
false,
|
||||
OC_Mount_Config::MOUNT_TYPE_GROUP,
|
||||
self::TEST_GROUP2,
|
||||
self::TEST_USER1,
|
||||
false,
|
||||
),
|
||||
// user mount point
|
||||
array(
|
||||
true,
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
self::TEST_USER1,
|
||||
self::TEST_USER2,
|
||||
false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test mount points used at mount time, making sure
|
||||
* the configuration is prepared properly.
|
||||
*
|
||||
* @dataProvider mountDataProvider
|
||||
* @param bool $isPersonal true for personal mount point, false for system mount point
|
||||
* @param string $mountType mount type
|
||||
* @param string $applicable target user/group or "all"
|
||||
* @param string $testUser user for which to retrieve the mount points
|
||||
* @param bool $expectVisible whether to expect the mount point to be visible for $testUser
|
||||
*/
|
||||
public function testMount($isPersonal, $mountType, $applicable, $testUser, $expectVisible) {
|
||||
$mountConfig = array(
|
||||
'host' => 'someost',
|
||||
'user' => 'someuser',
|
||||
'password' => 'somepassword',
|
||||
'root' => 'someroot'
|
||||
);
|
||||
|
||||
// add mount point as "test" user
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$mountConfig,
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
// check mount points in the perspective of user $testUser
|
||||
\OC_User::setUserId($testUser);
|
||||
|
||||
$mountPoints = OC_Mount_Config::getAbsoluteMountPoints($testUser);
|
||||
if ($expectVisible) {
|
||||
$this->assertEquals(1, count($mountPoints));
|
||||
$this->assertTrue(isset($mountPoints['/' . self::TEST_USER1 . '/files/ext']));
|
||||
$this->assertEquals('\OC\Files\Storage\SMB', $mountPoints['/' . self::TEST_USER1 . '/files/ext']['class']);
|
||||
$this->assertEquals($mountConfig, $mountPoints['/' . self::TEST_USER1 . '/files/ext']['options']);
|
||||
}
|
||||
else {
|
||||
$this->assertEquals(0, count($mountPoints));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,10 @@ if(!isset($linkedItem['uid_owner']) || !isset($linkedItem['file_source'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$userId = $linkedItem['uid_owner'];
|
||||
$rootLinkItem = OCP\Share::resolveReShare($linkedItem);
|
||||
$userId = $rootLinkItem['uid_owner'];
|
||||
|
||||
OCP\JSON::checkUserExists($rootLinkItem['uid_owner']);
|
||||
\OC_Util::setupFS($userId);
|
||||
\OC\Files\Filesystem::initMountPoints($userId);
|
||||
$view = new \OC\Files\View('/' . $userId . '/files');
|
||||
@@ -86,4 +89,4 @@ try{
|
||||
} catch (\Exception $e) {
|
||||
\OC_Response::setStatus(500);
|
||||
\OC_Log::write('core', $e->getmessage(), \OC_Log::DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ if (version_compare($installedVersion, '0.3', '<')) {
|
||||
$shareType = OCP\Share::SHARE_TYPE_USER;
|
||||
$shareWith = $row['uid_shared_with'];
|
||||
}
|
||||
OCP\JSON::checkUserExists($row['uid_owner']);
|
||||
OC_User::setUserId($row['uid_owner']);
|
||||
//we need to setup the filesystem for the user, otherwise OC_FileSystem::getRoot will fail and break
|
||||
OC_Util::setupFS($row['uid_owner']);
|
||||
|
||||
@@ -11,16 +11,13 @@
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
input[type="submit"]{
|
||||
input[type='submit'] {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
margin: 6px;
|
||||
background-image: url('%webroot%/core/img/actions/confirm.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
#body-login input[type="submit"] {
|
||||
#body-login input[type='submit'] {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
@media only screen and (max-width: 600px) {
|
||||
|
||||
/* make header scroll up for single shares, more view of content on small screens */
|
||||
#header.share-file {
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
/* hide size and date columns */
|
||||
table th#headerSize,
|
||||
table td.filesize,
|
||||
table th#headerDate,
|
||||
table td.date {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* restrict length of displayed filename to prevent overflow */
|
||||
table td.filename .nametext {
|
||||
max-width: 75% !important;
|
||||
}
|
||||
|
||||
/* on mobile, show single shared image at full width without margin */
|
||||
#imgframe {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
/* some margin for the file type icon */
|
||||
#imgframe .publicpreview {
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
/* always show actions on mobile */
|
||||
#fileList a.action {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)" !important;
|
||||
filter: alpha(opacity=20) !important;
|
||||
opacity: .2 !important;
|
||||
display: inline !important;
|
||||
}
|
||||
/* some padding for better clickability */
|
||||
#fileList a.action img {
|
||||
padding: 0 6px 0 12px;
|
||||
}
|
||||
/* hide text of the actions on mobile */
|
||||
#fileList a.action span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
body {
|
||||
background:#ddd;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#header {
|
||||
background: #1d2d44 url('%webroot%/core/img/noise.png') repeat;
|
||||
background-color: #1d2d44;
|
||||
height:32px;
|
||||
left:0;
|
||||
line-height:32px;
|
||||
@@ -14,47 +14,27 @@ body {
|
||||
padding:7px;
|
||||
}
|
||||
|
||||
#details {
|
||||
color:#fff;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#public_upload,
|
||||
#download {
|
||||
font-weight:700;
|
||||
margin: 0 0.4em 0 0;
|
||||
padding: 0 5px;
|
||||
height: 32px;
|
||||
float: left;
|
||||
|
||||
}
|
||||
|
||||
.header-right #details {
|
||||
margin-right: 28px;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
padding: 0;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
#public_upload {
|
||||
margin-left: 0.3em;
|
||||
#details {
|
||||
color:#fff;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
|
||||
filter: alpha(opacity=50);
|
||||
opacity: .5;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
#public_upload img,
|
||||
#download img {
|
||||
padding-left:.1em;
|
||||
padding-right:.3em;
|
||||
vertical-align:text-bottom;
|
||||
#controls {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#preview {
|
||||
background:#eee;
|
||||
border-bottom:1px solid #f8f8f8;
|
||||
min-height:30em;
|
||||
text-align:center;
|
||||
margin:45px auto;
|
||||
background: #fff;
|
||||
text-align: center;
|
||||
margin: 45px auto 0;
|
||||
}
|
||||
|
||||
#noPreview {
|
||||
@@ -62,11 +42,15 @@ body {
|
||||
padding-top:5em;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 45px;
|
||||
}
|
||||
|
||||
p.info {
|
||||
color:#777;
|
||||
text-align:center;
|
||||
width:22em;
|
||||
margin:2em auto;
|
||||
color: #777;
|
||||
text-align: center;
|
||||
margin: 0 auto;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
p.info a {
|
||||
@@ -87,9 +71,13 @@ p.info a {
|
||||
max-width:100%;
|
||||
}
|
||||
|
||||
thead{
|
||||
background-color: white;
|
||||
padding-left:0 !important; /* fixes multiselect bar offset on shared page */
|
||||
/* some margin for the file type icon */
|
||||
#imgframe .publicpreview {
|
||||
margin-top: 10%;
|
||||
}
|
||||
|
||||
thead {
|
||||
padding-left: 0 !important; /* fixes multiselect bar offset on shared page */
|
||||
}
|
||||
|
||||
#data-upload-form {
|
||||
@@ -103,41 +91,37 @@ thead{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#file_upload_start {
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
||||
filter: alpha(opacity=0);
|
||||
opacity: 0;
|
||||
z-index: 20;
|
||||
position: absolute !important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
#publicUploadButtonMock {
|
||||
position:relative;
|
||||
display:block;
|
||||
width:100%;
|
||||
height:32px;
|
||||
cursor:pointer;
|
||||
z-index:10;
|
||||
background-image:url('%webroot%/core/img/actions/upload.svg');
|
||||
background-repeat:no-repeat;
|
||||
background-position:7px 8px;
|
||||
}
|
||||
|
||||
#publicUploadButtonMock span {
|
||||
margin: 0 5px 0 28px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.directDownload,
|
||||
.directLink {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.directLink label {
|
||||
font-weight: normal;
|
||||
}
|
||||
.directLink input {
|
||||
margin-left: 10px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
/* keep long file names in one line to not overflow download button on mobile */
|
||||
.directDownload #download {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 90%;
|
||||
display: inline-block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.directDownload .button img {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.directLink label {
|
||||
font-weight: normal;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
|
||||
filter: alpha(opacity=50);
|
||||
opacity: .5;
|
||||
}
|
||||
.directLink input {
|
||||
margin-left: 5px;
|
||||
width: 300px;
|
||||
max-width: 90%;
|
||||
}
|
||||
.public_actions {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
@@ -9,39 +9,34 @@ function fileDownloadPath(dir, file) {
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#data-upload-form').tipsy({gravity:'ne', fade:true});
|
||||
|
||||
if (typeof FileActions !== 'undefined') {
|
||||
var mimetype = $('#mimetype').val();
|
||||
// Show file preview if previewer is available, images are already handled by the template
|
||||
if (mimetype.substr(0, mimetype.indexOf('/')) != 'image' && $('.publicpreview').length === 0) {
|
||||
// Trigger default action if not download TODO
|
||||
var action = FileActions.getDefault(mimetype, 'file', OC.PERMISSION_READ);
|
||||
if (typeof action === 'undefined') {
|
||||
$('#noPreview').show();
|
||||
if (mimetype != 'httpd/unix-directory') {
|
||||
// NOTE: Remove when a better file previewer solution exists
|
||||
$('#content').remove();
|
||||
$('table').remove();
|
||||
}
|
||||
} else {
|
||||
if (typeof action !== 'undefined') {
|
||||
action($('#filename').val());
|
||||
}
|
||||
}
|
||||
FileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function(filename) {
|
||||
var tr = $('tr').filterAttr('data-file', filename);
|
||||
var tr = FileList.findFileEl(filename);
|
||||
if (tr.length > 0) {
|
||||
window.location = $(tr).find('a.name').attr('href');
|
||||
}
|
||||
});
|
||||
FileActions.register('file', 'Download', OC.PERMISSION_READ, '', function(filename) {
|
||||
var tr = $('tr').filterAttr('data-file', filename);
|
||||
FileActions.register('file', 'Download', OC.PERMISSION_READ, function() {
|
||||
return OC.imagePath('core', 'actions/download');
|
||||
}, function(filename) {
|
||||
var tr = FileList.findFileEl(filename);
|
||||
if (tr.length > 0) {
|
||||
window.location = $(tr).find('a.name').attr('href');
|
||||
}
|
||||
});
|
||||
FileActions.register('dir', 'Download', OC.PERMISSION_READ, '', function(filename) {
|
||||
var tr = $('tr').filterAttr('data-file', filename);
|
||||
FileActions.register('dir', 'Download', OC.PERMISSION_READ, function() {
|
||||
return OC.imagePath('core', 'actions/download');
|
||||
}, function(filename) {
|
||||
var tr = FileList.findFileEl(filename);
|
||||
if (tr.length > 0) {
|
||||
window.location = $(tr).find('a.name').attr('href')+'&download';
|
||||
}
|
||||
@@ -58,15 +53,9 @@ $(document).ready(function() {
|
||||
};
|
||||
});
|
||||
|
||||
// Add Uploadprogress Wrapper to controls bar
|
||||
$('#controls').append($('#additional_controls div#uploadprogresswrapper'));
|
||||
|
||||
// Cancel upload trigger
|
||||
$('#cancel_upload_button').click(function() {
|
||||
OC.Upload.cancelUploads();
|
||||
procesSelection();
|
||||
$(document).on('click', '#directLink', function() {
|
||||
$(this).focus();
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
$('#directLink').focus();
|
||||
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ $(document).ready(function() {
|
||||
} else {
|
||||
var item = $('#dir').val() + '/' + filename;
|
||||
}
|
||||
var tr = $('tr').filterAttr('data-file', filename);
|
||||
var tr = FileList.findFileEl(filename);
|
||||
if ($(tr).data('type') == 'dir') {
|
||||
var itemType = 'folder';
|
||||
} else {
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
$TRANSLATIONS = array(
|
||||
"Password" => "Пароль",
|
||||
"Download" => "Загрузка",
|
||||
"Upload" => "Загрузка",
|
||||
"Cancel upload" => "Отмена загрузки"
|
||||
);
|
||||
$PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);";
|
||||
@@ -68,7 +68,7 @@ class Api {
|
||||
public static function getShare($params) {
|
||||
|
||||
$s = self::getShareFromId($params['id']);
|
||||
$params['itemSource'] = $s['item_source'];
|
||||
$params['itemSource'] = $s['file_source'];
|
||||
$params['itemType'] = $s['item_type'];
|
||||
$params['specificShare'] = true;
|
||||
|
||||
@@ -98,8 +98,14 @@ class Api {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$path = $params['path'];
|
||||
foreach ($shares as $key => $share) {
|
||||
$shares[$key]['path'] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// include also reshares in the lists. This means that the result
|
||||
// will contain every user with access to the file.
|
||||
if (isset($params['reshares']) && $params['reshares'] === true) {
|
||||
@@ -107,8 +113,10 @@ class Api {
|
||||
}
|
||||
|
||||
if ($receivedFrom) {
|
||||
$shares['received_from'] = $receivedFrom['uid_owner'];
|
||||
$shares['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']);
|
||||
foreach ($shares as $key => $share) {
|
||||
$shares[$key]['received_from'] = $receivedFrom['uid_owner'];
|
||||
$shares[$key]['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$shares = null;
|
||||
@@ -162,7 +170,7 @@ class Api {
|
||||
$view = new \OC\Files\View('/'.\OCP\User::getUser().'/files');
|
||||
|
||||
if(!$view->is_dir($path)) {
|
||||
return new \OC_OCS_Result(null, 404, "not a directory");
|
||||
return new \OC_OCS_Result(null, 400, "not a directory");
|
||||
}
|
||||
|
||||
$content = $view->getDirectoryContent($path);
|
||||
@@ -172,14 +180,16 @@ class Api {
|
||||
// workaround because folders are named 'dir' in this context
|
||||
$itemType = $file['type'] === 'file' ? 'file' : 'folder';
|
||||
$share = \OCP\Share::getItemShared($itemType, $file['fileid']);
|
||||
$receivedFrom = \OCP\Share::getItemSharedWithBySource($itemType, $file['fileid']);
|
||||
if ($receivedFrom) {
|
||||
$share['received_from'] = $receivedFrom['uid_owner'];
|
||||
$share['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']);
|
||||
}
|
||||
if ($share) {
|
||||
$share['filename'] = $file['name'];
|
||||
$result[] = $share;
|
||||
if($share) {
|
||||
$receivedFrom = \OCP\Share::getItemSharedWithBySource($itemType, $file['fileid']);
|
||||
reset($share);
|
||||
$key = key($share);
|
||||
$share[$key]['path'] = self::correctPath($share[$key]['path'], $path);
|
||||
if ($receivedFrom) {
|
||||
$share[$key]['received_from'] = $receivedFrom['uid_owner'];
|
||||
$share[$key]['received_from_displayname'] = \OCP\User::getDisplayName($receivedFrom['uid_owner']);
|
||||
}
|
||||
$result = array_merge($result, $share);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,10 +230,8 @@ class Api {
|
||||
$shareWith = isset($_POST['password']) ? $_POST['password'] : null;
|
||||
//check public link share
|
||||
$publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes');
|
||||
$encryptionEnabled = \OC_App::isEnabled('files_encryption');
|
||||
if(isset($_POST['publicUpload']) &&
|
||||
($encryptionEnabled || $publicUploadEnabled !== 'yes')) {
|
||||
return new \OC_OCS_Result(null, 404, "public upload disabled by the administrator");
|
||||
if(isset($_POST['publicUpload']) && $publicUploadEnabled !== 'yes') {
|
||||
return new \OC_OCS_Result(null, 403, "public upload disabled by the administrator");
|
||||
}
|
||||
$publicUpload = isset($_POST['publicUpload']) ? $_POST['publicUpload'] : 'false';
|
||||
// read, create, update (7) if public upload is enabled or
|
||||
@@ -231,7 +239,7 @@ class Api {
|
||||
$permissions = $publicUpload === 'true' ? 7 : 1;
|
||||
break;
|
||||
default:
|
||||
return new \OC_OCS_Result(null, 404, "unknown share type");
|
||||
return new \OC_OCS_Result(null, 400, "unknown share type");
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -243,7 +251,7 @@ class Api {
|
||||
$permissions
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
return new \OC_OCS_Result(null, 404, $e->getMessage());
|
||||
return new \OC_OCS_Result(null, 403, $e->getMessage());
|
||||
}
|
||||
|
||||
if ($token) {
|
||||
@@ -284,9 +292,8 @@ class Api {
|
||||
public static function updateShare($params) {
|
||||
|
||||
$share = self::getShareFromId($params['id']);
|
||||
$itemSource = isset($share['item_source']) ? $share['item_source'] : null;
|
||||
|
||||
if($itemSource === null) {
|
||||
if(!isset($share['file_source'])) {
|
||||
return new \OC_OCS_Result(null, 404, "wrong share Id, share doesn't exist.");
|
||||
}
|
||||
|
||||
@@ -321,11 +328,8 @@ class Api {
|
||||
$permissions = isset($params['_put']['permissions']) ? (int)$params['_put']['permissions'] : null;
|
||||
|
||||
$publicUploadStatus = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes');
|
||||
$encryptionEnabled = \OC_App::isEnabled('files_encryption');
|
||||
$publicUploadEnabled = false;
|
||||
if(!$encryptionEnabled && $publicUploadStatus === 'yes') {
|
||||
$publicUploadEnabled = true;
|
||||
}
|
||||
$publicUploadEnabled = ($publicUploadStatus === 'yes') ? true : false;
|
||||
|
||||
|
||||
// only change permissions for public shares if public upload is enabled
|
||||
// and we want to set permissions to 1 (read only) or 7 (allow upload)
|
||||
@@ -363,9 +367,8 @@ class Api {
|
||||
private static function updatePublicUpload($share, $params) {
|
||||
|
||||
$publicUploadEnabled = \OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes');
|
||||
$encryptionEnabled = \OC_App::isEnabled('files_encryption');
|
||||
if($encryptionEnabled || $publicUploadEnabled !== 'yes') {
|
||||
return new \OC_OCS_Result(null, 404, "public upload disabled by the administrator");
|
||||
if($publicUploadEnabled !== 'yes') {
|
||||
return new \OC_OCS_Result(null, 403, "public upload disabled by the administrator");
|
||||
}
|
||||
|
||||
if ($share['item_type'] !== 'folder' ||
|
||||
@@ -438,10 +441,10 @@ class Api {
|
||||
public static function deleteShare($params) {
|
||||
|
||||
$share = self::getShareFromId($params['id']);
|
||||
$itemSource = isset($share['item_source']) ? $share['item_source'] : null;
|
||||
$fileSource = isset($share['file_source']) ? $share['file_source'] : null;
|
||||
$itemType = isset($share['item_type']) ? $share['item_type'] : null;;
|
||||
|
||||
if($itemSource === null) {
|
||||
if($fileSource === null) {
|
||||
return new \OC_OCS_Result(null, 404, "wrong share ID, share doesn't exist.");
|
||||
}
|
||||
|
||||
@@ -455,7 +458,7 @@ class Api {
|
||||
try {
|
||||
$return = \OCP\Share::unshare(
|
||||
$itemType,
|
||||
$itemSource,
|
||||
$fileSource,
|
||||
$shareType,
|
||||
$shareWith);
|
||||
} catch (\Exception $e) {
|
||||
@@ -511,7 +514,7 @@ class Api {
|
||||
* @return array with: item_source, share_type, share_with, item_type, permissions
|
||||
*/
|
||||
private static function getShareFromId($shareID) {
|
||||
$sql = 'SELECT `item_source`, `share_type`, `share_with`, `item_type`, `permissions` FROM `*PREFIX*share` WHERE `id` = ?';
|
||||
$sql = 'SELECT `file_source`, `item_source`, `share_type`, `share_with`, `item_type`, `permissions` FROM `*PREFIX*share` WHERE `id` = ?';
|
||||
$args = array($shareID);
|
||||
$query = \OCP\DB::prepare($sql);
|
||||
$result = $query->execute($args);
|
||||
@@ -528,4 +531,15 @@ class Api {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief make sure that the path has the correct root
|
||||
*
|
||||
* @param string $path path returned from the share API
|
||||
* @param string $folder current root folder
|
||||
* @return string the correct path
|
||||
*/
|
||||
protected static function correctPath($path, $folder) {
|
||||
return \OC_Filesystem::normalizePath('/' . $folder . '/' . basename($path));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
namespace OC\Files\Cache;
|
||||
|
||||
use OCP\Share_Backend_Collection;
|
||||
|
||||
/**
|
||||
@@ -47,7 +48,7 @@ class Shared_Cache extends Cache {
|
||||
\OC\Files\Filesystem::initMountPoints($source['fileOwner']);
|
||||
$mount = \OC\Files\Filesystem::getMountByNumericId($source['storage']);
|
||||
if (is_array($mount)) {
|
||||
$fullPath = $mount[key($mount)]->getMountPoint().$source['path'];
|
||||
$fullPath = $mount[key($mount)]->getMountPoint() . $source['path'];
|
||||
list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($fullPath);
|
||||
if ($storage) {
|
||||
$this->files[$target] = $internalPath;
|
||||
@@ -72,7 +73,7 @@ class Shared_Cache extends Cache {
|
||||
/**
|
||||
* get the stored metadata of a file or folder
|
||||
*
|
||||
* @param string/int $file
|
||||
* @param string /int $file
|
||||
* @return array
|
||||
*/
|
||||
public function get($file) {
|
||||
@@ -92,12 +93,11 @@ class Shared_Cache extends Cache {
|
||||
} else {
|
||||
$query = \OC_DB::prepare(
|
||||
'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`,'
|
||||
.' `size`, `mtime`, `encrypted`'
|
||||
.' FROM `*PREFIX*filecache` WHERE `fileid` = ?');
|
||||
. ' `size`, `mtime`, `encrypted`, `unencrypted_size`'
|
||||
. ' FROM `*PREFIX*filecache` WHERE `fileid` = ?');
|
||||
$result = $query->execute(array($file));
|
||||
$data = $result->fetchRow();
|
||||
$data['fileid'] = (int)$data['fileid'];
|
||||
$data['size'] = (int)$data['size'];
|
||||
$data['mtime'] = (int)$data['mtime'];
|
||||
$data['storage_mtime'] = (int)$data['storage_mtime'];
|
||||
$data['encrypted'] = (bool)$data['encrypted'];
|
||||
@@ -106,6 +106,12 @@ class Shared_Cache extends Cache {
|
||||
if ($data['storage_mtime'] === 0) {
|
||||
$data['storage_mtime'] = $data['mtime'];
|
||||
}
|
||||
if ($data['encrypted'] or ($data['unencrypted_size'] > 0 and $data['mimetype'] === 'httpd/unix-directory')) {
|
||||
$data['encrypted_size'] = (int)$data['size'];
|
||||
$data['size'] = (int)$data['unencrypted_size'];
|
||||
} else {
|
||||
$data['size'] = (int)$data['size'];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
return false;
|
||||
@@ -123,11 +129,18 @@ class Shared_Cache extends Cache {
|
||||
foreach ($files as &$file) {
|
||||
$file['mimetype'] = $this->getMimetype($file['mimetype']);
|
||||
$file['mimepart'] = $this->getMimetype($file['mimepart']);
|
||||
$file['usersPath'] = 'files/Shared/' . ltrim($file['path'], '/');
|
||||
}
|
||||
return $files;
|
||||
} else {
|
||||
if ($cache = $this->getSourceCache($folder)) {
|
||||
return $cache->getFolderContents($this->files[$folder]);
|
||||
$cache = $this->getSourceCache($folder);
|
||||
if ($cache) {
|
||||
$sourceFolderContent = $cache->getFolderContents($this->files[$folder]);
|
||||
foreach ($sourceFolderContent as $key => $c) {
|
||||
$sourceFolderContent[$key]['usersPath'] = 'files/Shared/' . $folder . '/' . $c['name'];
|
||||
}
|
||||
|
||||
return $sourceFolderContent;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -248,19 +261,39 @@ class Shared_Cache extends Cache {
|
||||
* @return array
|
||||
*/
|
||||
public function searchByMime($mimetype) {
|
||||
|
||||
if (strpos($mimetype, '/')) {
|
||||
$where = '`mimetype` = ? AND ';
|
||||
} else {
|
||||
$where = '`mimepart` = ? AND ';
|
||||
$mimepart = null;
|
||||
if (strpos($mimetype, '/') === false) {
|
||||
$mimepart = $mimetype;
|
||||
$mimetype = null;
|
||||
}
|
||||
|
||||
$value = $this->getMimetypeId($mimetype);
|
||||
|
||||
return $this->searchWithWhere($where, $value);
|
||||
// note: searchWithWhere is currently broken as it doesn't
|
||||
// recurse into subdirs nor returns the correct
|
||||
// file paths, so using getFolderContents() for now
|
||||
|
||||
$result = array();
|
||||
$exploreDirs = array('');
|
||||
while (count($exploreDirs) > 0) {
|
||||
$dir = array_pop($exploreDirs);
|
||||
$files = $this->getFolderContents($dir);
|
||||
// no results?
|
||||
if (!$files) {
|
||||
continue;
|
||||
}
|
||||
foreach ($files as $file) {
|
||||
if ($file['mimetype'] === 'httpd/unix-directory') {
|
||||
$exploreDirs[] = ltrim($dir . '/' . $file['name'], '/');
|
||||
} else if (($mimepart && $file['mimepart'] === $mimepart) || ($mimetype && $file['mimetype'] === $mimetype)) {
|
||||
// usersPath not reliable
|
||||
//$file['path'] = $file['usersPath'];
|
||||
$file['path'] = ltrim($dir . '/' . $file['name'], '/');
|
||||
$result[] = $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The maximum number of placeholders that can be used in an SQL query.
|
||||
* Value MUST be <= 1000 for oracle:
|
||||
@@ -268,7 +301,7 @@ class Shared_Cache extends Cache {
|
||||
* FIXME we should get this from doctrine as other DBs allow a lot more placeholders
|
||||
*/
|
||||
const MAX_SQL_CHUNK_SIZE = 1000;
|
||||
|
||||
|
||||
/**
|
||||
* search for files with a custom where clause and value
|
||||
* the $wherevalue will be array_merge()d with the file id chunks
|
||||
@@ -282,16 +315,16 @@ class Shared_Cache extends Cache {
|
||||
$ids = $this->getAll();
|
||||
|
||||
$files = array();
|
||||
|
||||
|
||||
// divide into chunks
|
||||
$chunks = array_chunk($ids, $chunksize);
|
||||
|
||||
|
||||
foreach ($chunks as $chunk) {
|
||||
$placeholders = join(',', array_fill(0, count($chunk), '?'));
|
||||
$sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
|
||||
`encrypted`, `unencrypted_size`, `etag`
|
||||
FROM `*PREFIX*filecache` WHERE ' . $sqlwhere . ' `fileid` IN (' . $placeholders . ')';
|
||||
|
||||
|
||||
$stmt = \OC_DB::prepare($sql);
|
||||
|
||||
$result = $stmt->execute(array_merge(array($wherevalue), $chunk));
|
||||
@@ -302,6 +335,10 @@ class Shared_Cache extends Cache {
|
||||
}
|
||||
$row['mimetype'] = $this->getMimetype($row['mimetype']);
|
||||
$row['mimepart'] = $this->getMimetype($row['mimepart']);
|
||||
if ($row['encrypted'] or ($row['unencrypted_size'] > 0 and $row['mimetype'] === 'httpd/unix-directory')) {
|
||||
$row['encrypted_size'] = $row['size'];
|
||||
$row['size'] = $row['unencrypted_size'];
|
||||
}
|
||||
$files[] = $row;
|
||||
}
|
||||
}
|
||||
@@ -356,4 +393,48 @@ class Shared_Cache extends Cache {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the path of a file on this storage by it's id
|
||||
*
|
||||
* @param int $id
|
||||
* @param string $pathEnd (optional) used internally for recursive calls
|
||||
* @return string | null
|
||||
*/
|
||||
public function getPathById($id, $pathEnd = '') {
|
||||
// direct shares are easy
|
||||
if ($path = $this->getShareById($id)) {
|
||||
return $path . $pathEnd;
|
||||
} else {
|
||||
// if the item is a direct share we try and get the path of the parent and append the name of the item to it
|
||||
list($parent, $name) = $this->getParentInfo($id);
|
||||
if ($parent > 0) {
|
||||
return $this->getPathById($parent, '/' . $name . $pathEnd);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getShareById($id) {
|
||||
$item = \OCP\Share::getItemSharedWithBySource('file', $id);
|
||||
if ($item) {
|
||||
return trim($item['file_target'], '/');
|
||||
}
|
||||
$item = \OCP\Share::getItemSharedWithBySource('folder', $id);
|
||||
if ($item) {
|
||||
return trim($item['file_target'], '/');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getParentInfo($id) {
|
||||
$sql = 'SELECT `parent`, `name` FROM `*PREFIX*filecache` WHERE `fileid` = ?';
|
||||
$query = \OC_DB::prepare($sql);
|
||||
$result = $query->execute(array($id));
|
||||
if ($row = $result->fetchRow()) {
|
||||
return array($row['parent'], $row['name']);
|
||||
} else {
|
||||
return array(-1, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,19 @@ class Shared_Permissions extends Permissions {
|
||||
}
|
||||
}
|
||||
|
||||
private function getFile($fileId, $user) {
|
||||
if ($fileId == -1) {
|
||||
return \OCP\PERMISSION_READ;
|
||||
}
|
||||
$source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE,
|
||||
null, false);
|
||||
if ($source) {
|
||||
return $source['permissions'];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set the permissions of a file
|
||||
*
|
||||
@@ -82,7 +95,7 @@ class Shared_Permissions extends Permissions {
|
||||
if ($parentId === -1) {
|
||||
return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_PERMISSIONS);
|
||||
}
|
||||
$permissions = $this->get($parentId, $user);
|
||||
$permissions = $this->getFile($parentId, $user);
|
||||
$query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `parent` = ?');
|
||||
$result = $query->execute(array($parentId));
|
||||
$filePermissions = array();
|
||||
|
||||
@@ -31,10 +31,12 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent {
|
||||
private $path;
|
||||
|
||||
public function isValidSource($itemSource, $uidOwner) {
|
||||
$query = \OC_DB::prepare('SELECT `name` FROM `*PREFIX*filecache` WHERE `fileid` = ?');
|
||||
$result = $query->execute(array($itemSource));
|
||||
if ($row = $result->fetchRow()) {
|
||||
$this->path = $row['name'];
|
||||
$path = \OC\Files\Filesystem::getPath($itemSource);
|
||||
if ($path) {
|
||||
// FIXME: attributes should not be set here,
|
||||
// keeping this pattern for now to avoid unexpected
|
||||
// regressions
|
||||
$this->path = basename($path);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -91,10 +93,17 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent {
|
||||
$file['name'] = basename($item['file_target']);
|
||||
$file['mimetype'] = $item['mimetype'];
|
||||
$file['mimepart'] = $item['mimepart'];
|
||||
$file['size'] = $item['size'];
|
||||
$file['mtime'] = $item['mtime'];
|
||||
$file['encrypted'] = $item['encrypted'];
|
||||
$file['etag'] = $item['etag'];
|
||||
$storage = \OC\Files\Filesystem::getStorage('/');
|
||||
$cache = $storage->getCache();
|
||||
if ($item['encrypted'] or ($item['unencrypted_size'] > 0 and $cache->getMimetype($item['mimetype']) === 'httpd/unix-directory')) {
|
||||
$file['size'] = $item['unencrypted_size'];
|
||||
$file['encrypted_size'] = $item['size'];
|
||||
} else {
|
||||
$file['size'] = $item['size'];
|
||||
}
|
||||
$files[] = $file;
|
||||
}
|
||||
return $files;
|
||||
@@ -172,7 +181,7 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent {
|
||||
$source['fileOwner'] = $fileOwner;
|
||||
return $source;
|
||||
}
|
||||
\OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -279,43 +279,26 @@ class Shared extends \OC\Files\Storage\Common {
|
||||
if ($this->isDeletable($path)) {
|
||||
list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
|
||||
return $storage->unlink($internalPath);
|
||||
} else if (dirname($path) == '/' || dirname($path) == '.') {
|
||||
// Unshare the file from the user if in the root of the Shared folder
|
||||
if ($this->is_dir($path)) {
|
||||
$itemType = 'folder';
|
||||
} else {
|
||||
$itemType = 'file';
|
||||
}
|
||||
return \OCP\Share::unshareFromSelf($itemType, $path);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function rename($path1, $path2) {
|
||||
// Check for partial files
|
||||
if (pathinfo($path1, PATHINFO_EXTENSION) === 'part') {
|
||||
if ($oldSource = $this->getSourcePath($path1)) {
|
||||
// Renaming/moving is only allowed within shared folders
|
||||
$pos1 = strpos($path1, '/', 1);
|
||||
$pos2 = strpos($path2, '/', 1);
|
||||
if ($pos1 !== false && $pos2 !== false && ($oldSource = $this->getSourcePath($path1))) {
|
||||
$newSource = $this->getSourcePath(dirname($path2)) . '/' . basename($path2);
|
||||
// Within the same folder, we only need UPDATE permissions
|
||||
if (dirname($path1) == dirname($path2) and $this->isUpdatable($path1)) {
|
||||
list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource);
|
||||
$newInternalPath = substr($oldInternalPath, 0, -5);
|
||||
list(, $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource);
|
||||
return $storage->rename($oldInternalPath, $newInternalPath);
|
||||
}
|
||||
} else {
|
||||
// Renaming/moving is only allowed within shared folders
|
||||
$pos1 = strpos($path1, '/', 1);
|
||||
$pos2 = strpos($path2, '/', 1);
|
||||
if ($pos1 !== false && $pos2 !== false && ($oldSource = $this->getSourcePath($path1))) {
|
||||
$newSource = $this->getSourcePath(dirname($path2)) . '/' . basename($path2);
|
||||
// Within the same folder, we only need UPDATE permissions
|
||||
if (dirname($path1) == dirname($path2) and $this->isUpdatable($path1)) {
|
||||
list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource);
|
||||
list(, $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource);
|
||||
return $storage->rename($oldInternalPath, $newInternalPath);
|
||||
// otherwise DELETE and CREATE permissions required
|
||||
} elseif ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) {
|
||||
$rootView = new \OC\Files\View('');
|
||||
return $rootView->rename($oldSource, $newSource);
|
||||
}
|
||||
// otherwise DELETE and CREATE permissions required
|
||||
} elseif ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) {
|
||||
$rootView = new \OC\Files\View('');
|
||||
return $rootView->rename($oldSource, $newSource);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -112,8 +112,12 @@ class Shared_Updater {
|
||||
*/
|
||||
static public function shareHook($params) {
|
||||
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
|
||||
$uidOwner = \OCP\User::getUser();
|
||||
$users = \OCP\Share::getUsersItemShared($params['itemType'], $params['fileSource'], $uidOwner, true);
|
||||
if (isset($params['uidOwner'])) {
|
||||
$uidOwner = $params['uidOwner'];
|
||||
} else {
|
||||
$uidOwner = \OCP\User::getUser();
|
||||
}
|
||||
$users = \OCP\Share::getUsersItemShared($params['itemType'], $params['fileSource'], $uidOwner, true, false);
|
||||
if (!empty($users)) {
|
||||
while (!empty($users)) {
|
||||
$reshareUsers = array();
|
||||
|
||||
@@ -35,7 +35,7 @@ function determineIcon($file, $sharingRoot, $sharingToken) {
|
||||
|
||||
if (isset($_GET['t'])) {
|
||||
$token = $_GET['t'];
|
||||
$linkItem = OCP\Share::getShareByToken($token);
|
||||
$linkItem = OCP\Share::getShareByToken($token, false);
|
||||
if (is_array($linkItem) && isset($linkItem['uid_owner'])) {
|
||||
// seems to be a valid share
|
||||
$type = $linkItem['item_type'];
|
||||
@@ -43,10 +43,10 @@ if (isset($_GET['t'])) {
|
||||
$shareOwner = $linkItem['uid_owner'];
|
||||
$path = null;
|
||||
$rootLinkItem = OCP\Share::resolveReShare($linkItem);
|
||||
$fileOwner = $rootLinkItem['uid_owner'];
|
||||
if (isset($fileOwner)) {
|
||||
if (isset($rootLinkItem['uid_owner'])) {
|
||||
OCP\JSON::checkUserExists($rootLinkItem['uid_owner']);
|
||||
OC_Util::tearDownFS();
|
||||
OC_Util::setupFS($fileOwner);
|
||||
OC_Util::setupFS($rootLinkItem['uid_owner']);
|
||||
$path = \OC\Files\Filesystem::getPath($linkItem['file_source']);
|
||||
}
|
||||
}
|
||||
@@ -136,6 +136,7 @@ if (isset($path)) {
|
||||
} else {
|
||||
OCP\Util::addScript('files', 'file-upload');
|
||||
OCP\Util::addStyle('files_sharing', 'public');
|
||||
OCP\Util::addStyle('files_sharing', 'mobile');
|
||||
OCP\Util::addScript('files_sharing', 'public');
|
||||
OCP\Util::addScript('files', 'fileactions');
|
||||
OCP\Util::addScript('files', 'jquery.iframe-transport');
|
||||
@@ -158,7 +159,6 @@ if (isset($path)) {
|
||||
if ($linkItem['item_type'] !== 'folder') {
|
||||
$allowPublicUploadEnabled = false;
|
||||
}
|
||||
$tmpl->assign('allowPublicUploadEnabled', $allowPublicUploadEnabled);
|
||||
$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
|
||||
$tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
|
||||
|
||||
@@ -188,8 +188,8 @@ if (isset($path)) {
|
||||
} else {
|
||||
$i['extension'] = '';
|
||||
}
|
||||
$i['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($i['mimetype']);
|
||||
}
|
||||
$i['isPreviewAvailable'] = \OC::$server->getPreviewManager()->isMimeSupported($i['mimetype']);
|
||||
$i['directory'] = $getPath;
|
||||
$i['permissions'] = OCP\PERMISSION_READ;
|
||||
$i['icon'] = determineIcon($i, $basePath, $token);
|
||||
@@ -224,7 +224,8 @@ if (isset($path)) {
|
||||
$folder->assign('fileList', $list->fetchPage());
|
||||
$folder->assign('breadcrumb', $breadcrumbNav->fetchPage());
|
||||
$folder->assign('dir', $getPath);
|
||||
$folder->assign('isCreatable', false);
|
||||
$folder->assign('isCreatable', $allowPublicUploadEnabled);
|
||||
$folder->assign('dirToken', $linkItem['token']);
|
||||
$folder->assign('permissions', OCP\PERMISSION_READ);
|
||||
$folder->assign('isPublic',true);
|
||||
$folder->assign('publicUploadEnabled', 'no');
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
<?php endif; ?>
|
||||
<p class="infield">
|
||||
<label for="password" class="infield"><?php p($l->t('Password')); ?></label>
|
||||
<input type="password" name="password" id="password" placeholder="" value="" autofocus />
|
||||
<input type="submit" value="" class="svg" />
|
||||
<input type="password" name="password" id="password"
|
||||
placeholder="" value=""
|
||||
autocomplete="off" autocapitalize="off" autocorrect="off"
|
||||
autofocus />
|
||||
<input type="submit" value="" class="svg icon icon-confirm" />
|
||||
</p>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
@@ -9,64 +9,14 @@
|
||||
<input type="hidden" name="sharingToken" value="<?php p($_['sharingToken']) ?>" id="sharingToken">
|
||||
<input type="hidden" name="filename" value="<?php p($_['filename']) ?>" id="filename">
|
||||
<input type="hidden" name="mimetype" value="<?php p($_['mimetype']) ?>" id="mimetype">
|
||||
<header><div id="header">
|
||||
<header><div id="header" class="icon icon-noise <?php p((isset($_['folder']) ? 'share-folder' : 'share-file')) ?>">
|
||||
<a href="<?php print_unescaped(link_to('', 'index.php')); ?>" title="" id="owncloud"><img class="svg"
|
||||
src="<?php print_unescaped(image_path('', 'logo-wide.svg')); ?>" alt="<?php p($theme->getName()); ?>" /></a>
|
||||
<div id="logo-claim" style="display:none;"><?php p($theme->getLogoClaim()); ?></div>
|
||||
<div class="header-right">
|
||||
<?php if (isset($_['folder'])): ?>
|
||||
<span id="details"><?php p($l->t('%s shared the folder %s with you',
|
||||
array($_['displayName'], $_['filename']))) ?></span>
|
||||
<?php else: ?>
|
||||
<span id="details"><?php p($l->t('%s shared the file %s with you',
|
||||
array($_['displayName'], $_['filename']))) ?></span>
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<?php if (!isset($_['folder']) || $_['allowZipDownload']): ?>
|
||||
<a href="<?php p($_['downloadURL']); ?>" class="button" id="download"><img
|
||||
class="svg" alt="Download" src="<?php print_unescaped(OCP\image_path("core", "actions/download.svg")); ?>"
|
||||
/><span><?php p($l->t('Download'))?></span></a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($_['allowPublicUploadEnabled']):?>
|
||||
|
||||
|
||||
<input type="hidden" id="publicUploadRequestToken" name="requesttoken" value="<?php p($_['requesttoken']) ?>" />
|
||||
<input type="hidden" id="dirToken" name="dirToken" value="<?php p($_['dirToken']) ?>" />
|
||||
<input type="hidden" id="uploadMaxFilesize" name="uploadMaxFilesize" value="<?php p($_['uploadMaxFilesize']) ?>" />
|
||||
<input type="hidden" id="uploadMaxHumanFilesize" name="uploadMaxHumanFilesize" value="<?php p($_['uploadMaxHumanFilesize']) ?>" />
|
||||
<input type="hidden" id="directory_path" name="directory_path" value="<?php p($_['directory_path']) ?>" />
|
||||
<?php if($_['uploadMaxFilesize'] >= 0):?>
|
||||
<input type="hidden" name="MAX_FILE_SIZE" id="max_upload"
|
||||
value="<?php p($_['uploadMaxFilesize']) ?>">
|
||||
<?php endif;?>
|
||||
|
||||
|
||||
<div id="data-upload-form" class="button" title="<?php p($l->t('Upload') . ' max. '.$_['uploadMaxHumanFilesize']) ?>">
|
||||
<input id="file_upload_start" type="file" name="files[]" data-url="<?php print_unescaped(OCP\Util::linkTo('files', 'ajax/upload.php')); ?>" multiple>
|
||||
<a href="#" id="publicUploadButtonMock" class="svg">
|
||||
<span><?php p($l->t('Upload'))?></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<span id="details"><?php p($l->t('shared by %s', array($_['displayName']))) ?></span>
|
||||
</div>
|
||||
|
||||
<div id="additional_controls" style="display:none">
|
||||
<div id="uploadprogresswrapper">
|
||||
<div id="uploadprogressbar"></div>
|
||||
<input id="cancel_upload_button" type="button" class="stop" style="display:none"
|
||||
value="<?php p($l->t('Cancel upload'));?>"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
</div></header>
|
||||
</div></header>
|
||||
<div id="content">
|
||||
<div id="preview">
|
||||
<?php if (isset($_['folder'])): ?>
|
||||
@@ -82,25 +32,28 @@
|
||||
<source src="<?php p($_['downloadURL']); ?>" type="<?php p($_['mimetype']); ?>" />
|
||||
</video>
|
||||
</div>
|
||||
<?php elseif (\OC\Preview::isMimeSupported($_['mimetype'])): ?>
|
||||
<div id="imgframe">
|
||||
<img src="<?php p(OCP\Util::linkToRoute( 'core_ajax_public_preview', array('x' => 500, 'y' => 500, 'file' => urlencode($_['directory_path']), 't' => $_['dirToken']))); ?>" class="publicpreview"/>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<ul id="noPreview">
|
||||
<li class="error">
|
||||
<?php p($l->t('No preview available for').' '.$_['filename']); ?><br />
|
||||
<a href="<?php p($_['downloadURL']); ?>" id="download"><img class="svg" alt="Download"
|
||||
src="<?php print_unescaped(OCP\image_path("core", "actions/download.svg")); ?>"
|
||||
/><?php p($l->t('Download'))?></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div id="imgframe">
|
||||
<?php $size = \OC\Preview::isMimeSupported($_['mimetype']) ? 500 : 128 ?>
|
||||
<img src="<?php p(OCP\Util::linkToRoute( 'core_ajax_public_preview', array('x' => $size, 'y' => $size, 'file' => urlencode($_['directory_path']), 't' => $_['dirToken']))); ?>" class="publicpreview"/>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="directLink"><label for="directLink"><?php p($l->t('Direct link')) ?></label><input id="directLink" type="text" readonly value="<?php p($_['downloadURL']); ?>"></input></div>
|
||||
<div class="directDownload">
|
||||
<a href="<?php p($_['downloadURL']); ?>" id="download" class="button">
|
||||
<img class="svg" alt="" src="<?php print_unescaped(OCP\image_path("core", "actions/download.svg")); ?>"/>
|
||||
<?php p($l->t('Download %s', array($_['filename'])))?>
|
||||
</a>
|
||||
</div>
|
||||
<div class="directLink">
|
||||
<label for="directLink"><?php p($l->t('Direct link')) ?></label>
|
||||
<input id="directLink" type="text" readonly value="<?php p($_['downloadURL']); ?>">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<footer>
|
||||
<p class="info">
|
||||
<?php print_unescaped($theme->getLongFooter()); ?>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
<p class="info">
|
||||
<?php print_unescaped($theme->getLongFooter()); ?>
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
@@ -29,23 +29,34 @@ use OCA\Files\Share;
|
||||
*/
|
||||
class Test_Files_Sharing_Api extends Test_Files_Sharing_Base {
|
||||
|
||||
const TEST_FOLDER_NAME = '/folder_share_api_test';
|
||||
|
||||
private static $tempStorage;
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->folder = '/folder_share_api_test';
|
||||
$this->folder = self::TEST_FOLDER_NAME;
|
||||
$this->subfolder = '/subfolder_share_api_test';
|
||||
$this->subsubfolder = '/subsubfolder_share_api_test';
|
||||
|
||||
$this->filename = 'share-api-test.txt';
|
||||
$this->filename = '/share-api-test.txt';
|
||||
|
||||
// save file with content
|
||||
$this->view->file_put_contents($this->filename, $this->data);
|
||||
$this->view->mkdir($this->folder);
|
||||
$this->view->file_put_contents($this->folder.'/'.$this->filename, $this->data);
|
||||
$this->view->mkdir($this->folder . $this->subfolder);
|
||||
$this->view->mkdir($this->folder . $this->subfolder . $this->subsubfolder);
|
||||
$this->view->file_put_contents($this->folder.$this->filename, $this->data);
|
||||
$this->view->file_put_contents($this->folder . $this->subfolder . $this->filename, $this->data);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
$this->view->unlink($this->filename);
|
||||
$this->view->deleteAll($this->folder);
|
||||
|
||||
self::$tempStorage = null;
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
@@ -286,6 +297,343 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief share a folder, than reshare a file within the shared folder and check if we construct the correct path
|
||||
* @medium
|
||||
*/
|
||||
function testGetShareFromFolderReshares() {
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$fileInfo1 = $this->view->getFileInfo($this->folder);
|
||||
$fileInfo2 = $this->view->getFileInfo($this->folder.'/'.$this->filename);
|
||||
$fileInfo3 = $this->view->getFileInfo($this->folder.'/' . $this->subfolder . '/' .$this->filename);
|
||||
|
||||
// share root folder to user2
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
|
||||
|
||||
// share was successful?
|
||||
$this->assertTrue($result);
|
||||
|
||||
// login as user2
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
// share file in root folder
|
||||
$result = \OCP\Share::shareItem('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1);
|
||||
// share was successful?
|
||||
$this->assertTrue(is_string($result));
|
||||
|
||||
// share file in subfolder
|
||||
$result = \OCP\Share::shareItem('file', $fileInfo3['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1);
|
||||
// share was successful?
|
||||
$this->assertTrue(is_string($result));
|
||||
|
||||
$testValues=array(
|
||||
array('query' => 'Shared/' . $this->folder,
|
||||
'expectedResult' => '/Shared' . $this->folder . $this->filename),
|
||||
array('query' => 'Shared/' . $this->folder . $this->subfolder,
|
||||
'expectedResult' => '/Shared' . $this->folder . $this->subfolder . $this->filename),
|
||||
);
|
||||
foreach ($testValues as $value) {
|
||||
|
||||
$_GET['path'] = $value['query'];
|
||||
$_GET['subfiles'] = 'true';
|
||||
|
||||
$result = Share\Api::getAllShares(array());
|
||||
|
||||
$this->assertTrue($result->succeeded());
|
||||
|
||||
// test should return one share within $this->folder
|
||||
$data = $result->getData();
|
||||
|
||||
$this->assertEquals($value['expectedResult'], $data[0]['path']);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
|
||||
\OCP\Share::unshare('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
|
||||
\OCP\Share::unshare('file', $fileInfo3['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
\OCP\Share::unshare('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief reshare a sub folder and check if we get the correct path
|
||||
* @medium
|
||||
*/
|
||||
function testGetShareFromSubFolderReShares() {
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$fileInfo = $this->view->getFileInfo($this->folder . $this->subfolder);
|
||||
|
||||
// share sub-folder to user2
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
|
||||
|
||||
// share was successful?
|
||||
$this->assertTrue($result);
|
||||
|
||||
// login as user2
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
// reshare subfolder
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1);
|
||||
|
||||
// share was successful?
|
||||
$this->assertTrue(is_string($result));
|
||||
|
||||
$_GET['path'] = '/Shared';
|
||||
$_GET['subfiles'] = 'true';
|
||||
|
||||
$result = Share\Api::getAllShares(array());
|
||||
|
||||
$this->assertTrue($result->succeeded());
|
||||
|
||||
// test should return one share within $this->folder
|
||||
$data = $result->getData();
|
||||
|
||||
// we should get exactly one result
|
||||
$this->assertEquals(1, count($data));
|
||||
|
||||
$expectedPath = '/Shared' . $this->subfolder;
|
||||
$this->assertEquals($expectedPath, $data[0]['path']);
|
||||
|
||||
// cleanup
|
||||
$result = \OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
|
||||
$this->assertTrue($result);
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
$result = \OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
|
||||
$this->assertTrue($result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief test re-re-share of folder if the path gets constructed correctly
|
||||
* @medium
|
||||
*/
|
||||
function testGetShareFromFolderReReShares() {
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$fileInfo1 = $this->view->getFileInfo($this->folder . $this->subfolder);
|
||||
$fileInfo2 = $this->view->getFileInfo($this->folder . $this->subfolder . $this->subsubfolder);
|
||||
|
||||
// share sub-folder to user2
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
|
||||
|
||||
// share was successful?
|
||||
$this->assertTrue($result);
|
||||
|
||||
// login as user2
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
// reshare subsubfolder
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER3, 31);
|
||||
// share was successful?
|
||||
$this->assertTrue($result);
|
||||
|
||||
// login as user3
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER3);
|
||||
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1);
|
||||
// share was successful?
|
||||
$this->assertTrue(is_string($result));
|
||||
|
||||
|
||||
$_GET['path'] = '/Shared';
|
||||
$_GET['subfiles'] = 'true';
|
||||
|
||||
$result = Share\Api::getAllShares(array());
|
||||
|
||||
$this->assertTrue($result->succeeded());
|
||||
|
||||
// test should return one share within $this->folder
|
||||
$data = $result->getData();
|
||||
|
||||
// we should get exactly one result
|
||||
$this->assertEquals(1, count($data));
|
||||
|
||||
$expectedPath = '/Shared' . $this->subsubfolder;
|
||||
$this->assertEquals($expectedPath, $data[0]['path']);
|
||||
|
||||
|
||||
// cleanup
|
||||
$result = \OCP\Share::unshare('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
|
||||
$this->assertTrue($result);
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
$result = \OCP\Share::unshare('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER3);
|
||||
$this->assertTrue($result);
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
$result = \OCP\Share::unshare('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
|
||||
$this->assertTrue($result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief test multiple shared folder if the path gets constructed correctly
|
||||
* @medium
|
||||
*/
|
||||
function testGetShareMultipleSharedFolder() {
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$fileInfo1 = $this->view->getFileInfo($this->folder);
|
||||
$fileInfo2 = $this->view->getFileInfo($this->folder . $this->subfolder);
|
||||
|
||||
|
||||
// share sub-folder to user2
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
|
||||
|
||||
// share was successful?
|
||||
$this->assertTrue($result);
|
||||
|
||||
// share folder to user2
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
|
||||
|
||||
// share was successful?
|
||||
$this->assertTrue($result);
|
||||
|
||||
|
||||
// login as user2
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1);
|
||||
// share was successful?
|
||||
$this->assertTrue(is_string($result));
|
||||
|
||||
|
||||
// ask for shared/subfolder
|
||||
$expectedPath1 = '/Shared' . $this->subfolder;
|
||||
$_GET['path'] = $expectedPath1;
|
||||
|
||||
$result1 = Share\Api::getAllShares(array());
|
||||
|
||||
$this->assertTrue($result1->succeeded());
|
||||
|
||||
// test should return one share within $this->folder
|
||||
$data1 = $result1->getData();
|
||||
$share1 = reset($data1);
|
||||
|
||||
// ask for shared/folder/subfolder
|
||||
$expectedPath2 = '/Shared' . $this->folder . $this->subfolder;
|
||||
$_GET['path'] = $expectedPath2;
|
||||
|
||||
$result2 = Share\Api::getAllShares(array());
|
||||
|
||||
$this->assertTrue($result2->succeeded());
|
||||
|
||||
// test should return one share within $this->folder
|
||||
$data2 = $result2->getData();
|
||||
$share2 = reset($data2);
|
||||
|
||||
|
||||
// validate results
|
||||
// we should get exactly one result each time
|
||||
$this->assertEquals(1, count($data1));
|
||||
$this->assertEquals(1, count($data2));
|
||||
|
||||
$this->assertEquals($expectedPath1, $share1['path']);
|
||||
$this->assertEquals($expectedPath2, $share2['path']);
|
||||
|
||||
|
||||
// cleanup
|
||||
$result = \OCP\Share::unshare('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
|
||||
$this->assertTrue($result);
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
$result = \OCP\Share::unshare('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
|
||||
$this->assertTrue($result);
|
||||
$result = \OCP\Share::unshare('folder', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
|
||||
$this->assertTrue($result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief test re-re-share of folder if the path gets constructed correctly
|
||||
* @medium
|
||||
*/
|
||||
function testGetShareFromFileReReShares() {
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$fileInfo1 = $this->view->getFileInfo($this->folder . $this->subfolder);
|
||||
$fileInfo2 = $this->view->getFileInfo($this->folder. $this->subfolder . $this->filename);
|
||||
|
||||
// share sub-folder to user2
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
|
||||
|
||||
// share was successful?
|
||||
$this->assertTrue($result);
|
||||
|
||||
// login as user2
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
// reshare subsubfolder
|
||||
$result = \OCP\Share::shareItem('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER3, 31);
|
||||
// share was successful?
|
||||
$this->assertTrue($result);
|
||||
|
||||
// login as user3
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER3);
|
||||
|
||||
$result = \OCP\Share::shareItem('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1);
|
||||
// share was successful?
|
||||
$this->assertTrue(is_string($result));
|
||||
|
||||
|
||||
$_GET['path'] = '/Shared';
|
||||
$_GET['subfiles'] = 'true';
|
||||
|
||||
$result = Share\Api::getAllShares(array());
|
||||
|
||||
$this->assertTrue($result->succeeded());
|
||||
|
||||
// test should return one share within $this->folder
|
||||
$data = $result->getData();
|
||||
|
||||
// we should get exactly one result
|
||||
$this->assertEquals(1, count($data));
|
||||
|
||||
$expectedPath = '/Shared' . $this->filename;
|
||||
$this->assertEquals($expectedPath, $data[0]['path']);
|
||||
|
||||
|
||||
// cleanup
|
||||
$result = \OCP\Share::unshare('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null);
|
||||
$this->assertTrue($result);
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
$result = \OCP\Share::unshare('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER3);
|
||||
$this->assertTrue($result);
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
$result = \OCP\Share::unshare('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
|
||||
$this->assertTrue($result);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
@@ -491,4 +839,107 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base {
|
||||
$this->assertTrue(empty($itemsAfterDelete));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief test unshare of a reshared file
|
||||
*/
|
||||
function testDeleteReshare() {
|
||||
|
||||
// user 1 shares a folder with user2
|
||||
\Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$fileInfo1 = $this->view->getFileInfo($this->folder);
|
||||
$fileInfo2 = $this->view->getFileInfo($this->folder.'/'.$this->filename);
|
||||
|
||||
$result1 = \OCP\Share::shareItem('folder', $fileInfo1['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
|
||||
|
||||
$this->assertTrue($result1);
|
||||
|
||||
// user2 shares a file from the folder as link
|
||||
\Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
$result2 = \OCP\Share::shareItem('file', $fileInfo2['fileid'], \OCP\Share::SHARE_TYPE_LINK, null, 1);
|
||||
|
||||
$this->assertTrue(is_string($result2));
|
||||
|
||||
// test if we can unshare the link again
|
||||
$items = \OCP\Share::getItemShared('file', null);
|
||||
$this->assertEquals(1, count($items));
|
||||
|
||||
$item = reset($items);
|
||||
$result3 = Share\Api::deleteShare(array('id' => $item['id']));
|
||||
|
||||
$this->assertTrue($result3->succeeded());
|
||||
|
||||
}
|
||||
|
||||
function testCorrectPath() {
|
||||
$path = "/foo/bar/test.txt";
|
||||
$folder = "/correct/path";
|
||||
$expectedResult = "/correct/path/test.txt";
|
||||
|
||||
$shareApiDummy = new TestShareApi();
|
||||
|
||||
$this->assertSame($expectedResult, $shareApiDummy->correctPathTest($path, $folder));
|
||||
}
|
||||
|
||||
/**
|
||||
* Post init mount points hook for mounting simulated ext storage
|
||||
*/
|
||||
public static function initTestMountPointsHook($data) {
|
||||
if ($data['user'] === \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1) {
|
||||
\OC\Files\Filesystem::mount(self::$tempStorage, array(), '/' . \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1 . '/files' . self::TEST_FOLDER_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests mounting a folder that is an external storage mount point.
|
||||
*/
|
||||
public function testShareStorageMountPoint() {
|
||||
self::$tempStorage = new \OC\Files\Storage\Temporary(array());
|
||||
self::$tempStorage->file_put_contents('test.txt', 'abcdef');
|
||||
self::$tempStorage->getScanner()->scan('');
|
||||
|
||||
// needed because the sharing code sometimes switches the user internally and mounts the user's
|
||||
// storages. In our case the temp storage isn't mounted automatically, so doing it in the post hook
|
||||
// (similar to how ext storage works)
|
||||
OCP\Util::connectHook('OC_Filesystem', 'post_initMountPoints', '\Test_Files_Sharing_Api', 'initTestMountPointsHook');
|
||||
|
||||
// logging in will auto-mount the temp storage for user1 as well
|
||||
\Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$fileInfo = $this->view->getFileInfo($this->folder);
|
||||
|
||||
// user 1 shares the mount point folder with user2
|
||||
$result = \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31);
|
||||
|
||||
$this->assertTrue($result);
|
||||
|
||||
// user2: check that mount point name appears correctly
|
||||
\Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
$view = new \OC\Files\View('/' . \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2 . '/files/Shared');
|
||||
|
||||
$this->assertTrue($view->file_exists($this->folder));
|
||||
$this->assertTrue($view->file_exists($this->folder . '/test.txt'));
|
||||
|
||||
\Test_Files_Sharing_Api::loginHelper(\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
\OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
\Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
\OC_Hook::clear('OC_Filesystem', 'post_initMountPoints', '\Test_Files_Sharing_Api', 'initTestMountPointsHook');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief dumnmy class to test protected methods
|
||||
*/
|
||||
class TestShareApi extends \OCA\Files\Share\Api {
|
||||
public function correctPathTest($path, $folder) {
|
||||
return self::correctPath($path, $folder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ abstract class Test_Files_Sharing_Base extends \PHPUnit_Framework_TestCase {
|
||||
*/
|
||||
public $view;
|
||||
public $folder;
|
||||
public $subfolder;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
// reset backend
|
||||
@@ -132,8 +133,8 @@ abstract class Test_Files_Sharing_Base extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$share = Null;
|
||||
|
||||
if ($result && $result->numRows() > 0) {
|
||||
$share = $result->fetchRow();
|
||||
if ($result) {
|
||||
$share = $result->fetchRow();
|
||||
}
|
||||
|
||||
return $share;
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Vincent Petry, Bjoern Schiessle
|
||||
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
||||
* 2014 Bjoern Schiessle <schiessle@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
require_once __DIR__ . '/base.php';
|
||||
|
||||
class Test_Files_Sharing_Cache extends Test_Files_Sharing_Base {
|
||||
|
||||
/**
|
||||
* @var OC_FilesystemView
|
||||
*/
|
||||
public $user2View;
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$this->user2View = new \OC\Files\View('/'. self::TEST_FILES_SHARING_API_USER2 . '/files');
|
||||
|
||||
// prepare user1's dir structure
|
||||
$this->view->mkdir('container');
|
||||
$this->view->mkdir('container/shareddir');
|
||||
$this->view->mkdir('container/shareddir/subdir');
|
||||
$this->view->mkdir('container/shareddir/emptydir');
|
||||
|
||||
$textData = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
$this->view->file_put_contents('container/not shared.txt', $textData);
|
||||
$this->view->file_put_contents('container/shared single file.txt', $textData);
|
||||
$this->view->file_put_contents('container/shareddir/bar.txt', $textData);
|
||||
$this->view->file_put_contents('container/shareddir/subdir/another.txt', $textData);
|
||||
$this->view->file_put_contents('container/shareddir/subdir/another too.txt', $textData);
|
||||
$this->view->file_put_contents('container/shareddir/subdir/not a text file.xml', '<xml></xml>');
|
||||
|
||||
list($this->ownerStorage, $internalPath) = $this->view->resolvePath('');
|
||||
$this->ownerCache = $this->ownerStorage->getCache();
|
||||
$this->ownerStorage->getScanner()->scan('');
|
||||
|
||||
// share "shareddir" with user2
|
||||
$fileinfo = $this->view->getFileInfo('container/shareddir');
|
||||
\OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
self::TEST_FILES_SHARING_API_USER2, 31);
|
||||
|
||||
$fileinfo = $this->view->getFileInfo('container/shared single file.txt');
|
||||
\OCP\Share::shareItem('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
self::TEST_FILES_SHARING_API_USER2, 31);
|
||||
|
||||
// login as user2
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
// retrieve the shared storage
|
||||
$secondView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2);
|
||||
list($this->sharedStorage, $internalPath) = $secondView->resolvePath('files/Shared/shareddir');
|
||||
$this->sharedCache = $this->sharedStorage->getCache();
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
$this->sharedCache->clear();
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$fileinfo = $this->view->getFileInfo('container/shareddir');
|
||||
\OCP\Share::unshare('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
$fileinfo = $this->view->getFileInfo('container/shared single file.txt');
|
||||
\OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
$this->view->deleteAll('container');
|
||||
|
||||
$this->ownerCache->clear();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test searching by mime type
|
||||
*/
|
||||
function testSearchByMime() {
|
||||
$results = $this->sharedStorage->getCache()->searchByMime('text');
|
||||
$check = array(
|
||||
array(
|
||||
'name' => 'shared single file.txt',
|
||||
'path' => 'shared single file.txt'
|
||||
),
|
||||
array(
|
||||
'name' => 'bar.txt',
|
||||
'path' => 'shareddir/bar.txt'
|
||||
),
|
||||
array(
|
||||
'name' => 'another too.txt',
|
||||
'path' => 'shareddir/subdir/another too.txt'
|
||||
),
|
||||
array(
|
||||
'name' => 'another.txt',
|
||||
'path' => 'shareddir/subdir/another.txt'
|
||||
),
|
||||
);
|
||||
$this->verifyFiles($check, $results);
|
||||
|
||||
$results2 = $this->sharedStorage->getCache()->searchByMime('text/plain');
|
||||
|
||||
$this->verifyFiles($check, $results);
|
||||
}
|
||||
|
||||
function testGetFolderContentsInRoot() {
|
||||
$results = $this->user2View->getDirectoryContent('/Shared/');
|
||||
|
||||
$this->verifyFiles(
|
||||
array(
|
||||
array(
|
||||
'name' => 'shareddir',
|
||||
'path' => '/shareddir',
|
||||
'mimetype' => 'httpd/unix-directory',
|
||||
'usersPath' => 'files/Shared/shareddir'
|
||||
),
|
||||
array(
|
||||
'name' => 'shared single file.txt',
|
||||
'path' => '/shared single file.txt',
|
||||
'mimetype' => 'text/plain',
|
||||
'usersPath' => 'files/Shared/shared single file.txt'
|
||||
),
|
||||
),
|
||||
$results
|
||||
);
|
||||
}
|
||||
|
||||
function testGetFolderContentsInSubdir() {
|
||||
$results = $this->user2View->getDirectoryContent('/Shared/shareddir');
|
||||
|
||||
$this->verifyFiles(
|
||||
array(
|
||||
array(
|
||||
'name' => 'bar.txt',
|
||||
'path' => 'files/container/shareddir/bar.txt',
|
||||
'mimetype' => 'text/plain',
|
||||
'usersPath' => 'files/Shared/shareddir/bar.txt'
|
||||
),
|
||||
array(
|
||||
'name' => 'emptydir',
|
||||
'path' => 'files/container/shareddir/emptydir',
|
||||
'mimetype' => 'httpd/unix-directory',
|
||||
'usersPath' => 'files/Shared/shareddir/emptydir'
|
||||
),
|
||||
array(
|
||||
'name' => 'subdir',
|
||||
'path' => 'files/container/shareddir/subdir',
|
||||
'mimetype' => 'httpd/unix-directory',
|
||||
'usersPath' => 'files/Shared/shareddir/subdir'
|
||||
),
|
||||
),
|
||||
$results
|
||||
);
|
||||
}
|
||||
|
||||
function testGetFolderContentsWhenSubSubdirShared() {
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$fileinfo = $this->view->getFileInfo('container/shareddir/subdir');
|
||||
\OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
self::TEST_FILES_SHARING_API_USER3, 31);
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER3);
|
||||
|
||||
$thirdView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files');
|
||||
$results = $thirdView->getDirectoryContent('/Shared/subdir');
|
||||
|
||||
$this->verifyFiles(
|
||||
array(
|
||||
array(
|
||||
'name' => 'another too.txt',
|
||||
'path' => 'files/container/shareddir/subdir/another too.txt',
|
||||
'mimetype' => 'text/plain',
|
||||
'usersPath' => 'files/Shared/subdir/another too.txt'
|
||||
),
|
||||
array(
|
||||
'name' => 'another.txt',
|
||||
'path' => 'files/container/shareddir/subdir/another.txt',
|
||||
'mimetype' => 'text/plain',
|
||||
'usersPath' => 'files/Shared/subdir/another.txt'
|
||||
),
|
||||
array(
|
||||
'name' => 'not a text file.xml',
|
||||
'path' => 'files/container/shareddir/subdir/not a text file.xml',
|
||||
'mimetype' => 'application/xml',
|
||||
'usersPath' => 'files/Shared/subdir/not a text file.xml'
|
||||
),
|
||||
),
|
||||
$results
|
||||
);
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
\OCP\Share::unshare('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
self::TEST_FILES_SHARING_API_USER3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if 'results' contains the expected 'examples' only.
|
||||
*
|
||||
* @param array $examples array of example files
|
||||
* @param array $results array of files
|
||||
*/
|
||||
private function verifyFiles($examples, $results) {
|
||||
$this->assertEquals(count($examples), count($results));
|
||||
|
||||
foreach ($examples as $example) {
|
||||
foreach ($results as $key => $result) {
|
||||
if ($result['name'] === $example['name']) {
|
||||
$this->verifyKeys($example, $result);
|
||||
unset($results[$key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->assertTrue(empty($results));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief verify if each value from the result matches the expected result
|
||||
* @param array $example array with the expected results
|
||||
* @param array $result array with the results
|
||||
*/
|
||||
private function verifyKeys($example, $result) {
|
||||
foreach ($example as $key => $value) {
|
||||
$this->assertEquals($value, $result[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetPathByIdDirectShare() {
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
\OC\Files\Filesystem::file_put_contents('test.txt', 'foo');
|
||||
$info = \OC\Files\Filesystem::getFileInfo('test.txt');
|
||||
\OCP\Share::shareItem('file', $info['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2, \OCP\PERMISSION_ALL);
|
||||
\OC_Util::tearDownFS();
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
$this->assertTrue(\OC\Files\Filesystem::file_exists('/Shared/test.txt'));
|
||||
list($sharedStorage) = \OC\Files\Filesystem::resolvePath('/' . self::TEST_FILES_SHARING_API_USER2 . '/files/Shared/test.txt');
|
||||
/**
|
||||
* @var \OC\Files\Storage\Shared $sharedStorage
|
||||
*/
|
||||
|
||||
$sharedCache = $sharedStorage->getCache();
|
||||
$this->assertEquals('test.txt', $sharedCache->getPathById($info['fileid']));
|
||||
}
|
||||
|
||||
public function testGetPathByIdShareSubFolder() {
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
\OC\Files\Filesystem::mkdir('foo');
|
||||
\OC\Files\Filesystem::mkdir('foo/bar');
|
||||
\OC\Files\Filesystem::touch('foo/bar/test.txt', 'bar');
|
||||
$folderInfo = \OC\Files\Filesystem::getFileInfo('foo');
|
||||
$fileInfo = \OC\Files\Filesystem::getFileInfo('foo/bar/test.txt');
|
||||
\OCP\Share::shareItem('folder', $folderInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2, \OCP\PERMISSION_ALL);
|
||||
\OC_Util::tearDownFS();
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
$this->assertTrue(\OC\Files\Filesystem::file_exists('/Shared/foo'));
|
||||
list($sharedStorage) = \OC\Files\Filesystem::resolvePath('/' . self::TEST_FILES_SHARING_API_USER2 . '/files/Shared/foo');
|
||||
/**
|
||||
* @var \OC\Files\Storage\Shared $sharedStorage
|
||||
*/
|
||||
|
||||
$sharedCache = $sharedStorage->getCache();
|
||||
$this->assertEquals('foo', $sharedCache->getPathById($folderInfo['fileid']));
|
||||
$this->assertEquals('foo/bar/test.txt', $sharedCache->getPathById($fileInfo['fileid']));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud
|
||||
*
|
||||
* @author Vincent Petry
|
||||
* @copyright 2013 Vincent Petry <pvince81@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
require_once __DIR__ . '/base.php';
|
||||
|
||||
class Test_Files_Sharing_Permissions extends Test_Files_Sharing_Base {
|
||||
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
// prepare user1's dir structure
|
||||
$textData = "dummy file data\n";
|
||||
$this->view->mkdir('container');
|
||||
$this->view->mkdir('container/shareddir');
|
||||
$this->view->mkdir('container/shareddir/subdir');
|
||||
$this->view->mkdir('container/shareddirrestricted');
|
||||
$this->view->mkdir('container/shareddirrestricted/subdir');
|
||||
$this->view->file_put_contents('container/shareddir/textfile.txt', $textData);
|
||||
$this->view->file_put_contents('container/shareddirrestricted/textfile1.txt', $textData);
|
||||
|
||||
list($this->ownerStorage, $internalPath) = $this->view->resolvePath('');
|
||||
$this->ownerCache = $this->ownerStorage->getCache();
|
||||
$this->ownerStorage->getScanner()->scan('');
|
||||
|
||||
// share "shareddir" with user2
|
||||
$fileinfo = $this->view->getFileInfo('container/shareddir');
|
||||
\OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
self::TEST_FILES_SHARING_API_USER2, 31);
|
||||
$fileinfo2 = $this->view->getFileInfo('container/shareddirrestricted');
|
||||
\OCP\Share::shareItem('folder', $fileinfo2['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
self::TEST_FILES_SHARING_API_USER2, 7);
|
||||
|
||||
// login as user2
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
// retrieve the shared storage
|
||||
$this->secondView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2);
|
||||
list($this->sharedStorage, $internalPath) = $this->secondView->resolvePath('files/Shared/shareddir');
|
||||
$this->sharedCache = $this->sharedStorage->getCache();
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
$this->sharedCache->clear();
|
||||
|
||||
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
|
||||
|
||||
$fileinfo = $this->view->getFileInfo('container/shareddir');
|
||||
\OCP\Share::unshare('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
self::TEST_FILES_SHARING_API_USER2);
|
||||
$fileinfo2 = $this->view->getFileInfo('container/shareddirrestricted');
|
||||
\OCP\Share::unshare('folder', $fileinfo2['fileid'], \OCP\Share::SHARE_TYPE_USER,
|
||||
self::TEST_FILES_SHARING_API_USER2);
|
||||
|
||||
$this->view->deleteAll('container');
|
||||
|
||||
$this->ownerCache->clear();
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the permissions of shared directory are returned correctly
|
||||
*/
|
||||
function testGetPermissions() {
|
||||
$sharedDirPerms = $this->sharedStorage->getPermissions('shareddir');
|
||||
$this->assertEquals(31, $sharedDirPerms);
|
||||
$sharedDirPerms = $this->sharedStorage->getPermissions('shareddir/textfile.txt');
|
||||
$this->assertEquals(31, $sharedDirPerms);
|
||||
$sharedDirRestrictedPerms = $this->sharedStorage->getPermissions('shareddirrestricted');
|
||||
$this->assertEquals(7, $sharedDirRestrictedPerms);
|
||||
$sharedDirRestrictedPerms = $this->sharedStorage->getPermissions('shareddirrestricted/textfile.txt');
|
||||
$this->assertEquals(7, $sharedDirRestrictedPerms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the permissions of shared directory are returned correctly
|
||||
*/
|
||||
function testGetDirectoryPermissions() {
|
||||
$contents = $this->secondView->getDirectoryContent('files/Shared/shareddir');
|
||||
$this->assertEquals('subdir', $contents[0]['name']);
|
||||
$this->assertEquals(31, $contents[0]['permissions']);
|
||||
$this->assertEquals('textfile.txt', $contents[1]['name']);
|
||||
$this->assertEquals(31, $contents[1]['permissions']);
|
||||
$contents = $this->secondView->getDirectoryContent('files/Shared/shareddirrestricted');
|
||||
$this->assertEquals('subdir', $contents[0]['name']);
|
||||
$this->assertEquals(7, $contents[0]['permissions']);
|
||||
$this->assertEquals('textfile1.txt', $contents[1]['name']);
|
||||
$this->assertEquals(7, $contents[1]['permissions']);
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,24 @@ OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
// "empty trash" command
|
||||
$deleteAll = false;
|
||||
if (isset($_POST['allfiles']) and $_POST['allfiles'] === 'true'){
|
||||
$user = \OCP\User::getUser();
|
||||
$list = OCA\Files_Trashbin\Helper::getTrashFiles('/');
|
||||
$deleteAll = true;
|
||||
$dirlisting = '0';
|
||||
$folder = isset($_POST['dir']) ? $_POST['dir'] : '/';
|
||||
if ($folder === '/' || $folder === '') {
|
||||
OCA\Files_Trashbin\Trashbin::deleteAll();
|
||||
$list = array();
|
||||
} else {
|
||||
$dirname = dirname($folder);
|
||||
if ( $dirname !== '/' && $dirname !== '.' ) {
|
||||
$dirlisting = '1';
|
||||
} else {
|
||||
$dirlisting = '0';
|
||||
}
|
||||
$list[] = $folder;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$deleteAll = false;
|
||||
$files = $_POST['files'];
|
||||
$dirlisting = $_POST['dirlisting'];
|
||||
$list = json_decode($files);
|
||||
@@ -19,19 +29,13 @@ else {
|
||||
$error = array();
|
||||
$success = array();
|
||||
|
||||
|
||||
$i = 0;
|
||||
foreach ($list as $file) {
|
||||
if ( $dirlisting === '0') {
|
||||
if ($deleteAll) {
|
||||
$filename = $file['name'];
|
||||
$timestamp = $file['timestamp'];
|
||||
}
|
||||
else {
|
||||
$delimiter = strrpos($file, '.d');
|
||||
$filename = substr($file, 0, $delimiter);
|
||||
$timestamp = substr($file, $delimiter+2);
|
||||
}
|
||||
$file = ltrim($file, '/');
|
||||
$delimiter = strrpos($file, '.d');
|
||||
$filename = substr($file, 0, $delimiter);
|
||||
$timestamp = substr($file, $delimiter+2);
|
||||
} else {
|
||||
$filename = $file;
|
||||
$timestamp = null;
|
||||
|
||||
@@ -34,7 +34,17 @@ try{
|
||||
if ($view->is_dir($file)) {
|
||||
$mimetype = 'httpd/unix-directory';
|
||||
} else {
|
||||
$mimetype = \OC_Helper::getFileNameMimeType(pathinfo($file, PATHINFO_FILENAME));
|
||||
$pathInfo = pathinfo($file);
|
||||
$fileName = $pathInfo['basename'];
|
||||
// if in root dir
|
||||
if ($pathInfo['dirname'] === '.') {
|
||||
// cut off the .d* suffix
|
||||
$i = strrpos($fileName, '.');
|
||||
if ($i !== false) {
|
||||
$fileName = substr($fileName, 0, $i);
|
||||
}
|
||||
}
|
||||
$mimetype = \OC_Helper::getFileNameMimeType($fileName);
|
||||
}
|
||||
$preview->setMimetype($mimetype);
|
||||
$preview->setMaxX($maxX);
|
||||
@@ -45,4 +55,4 @@ try{
|
||||
}catch(\Exception $e) {
|
||||
\OC_Response::setStatus(500);
|
||||
\OC_Log::write('core', $e->getmessage(), \OC_Log::DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ $success = array();
|
||||
$i = 0;
|
||||
foreach ($list as $file) {
|
||||
if ( $dirlisting === '0') {
|
||||
$file = ltrim($file, '/');
|
||||
$delimiter = strrpos($file, '.d');
|
||||
$filename = substr($file, 0, $delimiter);
|
||||
$timestamp = substr($file, $delimiter+2);
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<notnull>true</notnull>
|
||||
<length>512</length>
|
||||
</field>
|
||||
|
||||
|
||||
<field>
|
||||
<name>type</name>
|
||||
<type>text</type>
|
||||
@@ -52,15 +52,15 @@
|
||||
<notnull>true</notnull>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
|
||||
<field>
|
||||
<name>mime</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>30</length>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
|
||||
<index>
|
||||
<name>id_index</name>
|
||||
<field>
|
||||
@@ -68,7 +68,7 @@
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
|
||||
<index>
|
||||
<name>timestamp_index</name>
|
||||
<field>
|
||||
@@ -76,7 +76,7 @@
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
|
||||
<index>
|
||||
<name>user_index</name>
|
||||
<field>
|
||||
@@ -84,7 +84,7 @@
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
@@ -110,7 +110,7 @@
|
||||
<notnull>true</notnull>
|
||||
<length>50</length>
|
||||
</field>
|
||||
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.4
|
||||
0.5
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#fileList td a.file, #fileList td a.file span {
|
||||
cursor: default;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - trash bin
|
||||
*
|
||||
* @author Bjoern Schiessle
|
||||
* @copyright 2013 Bjoern Schiessle schiessle@owncloud.com
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Check if we are a user
|
||||
OCP\User::checkLoggedIn();
|
||||
|
||||
$filename = $_GET["file"];
|
||||
|
||||
$view = new OC_FilesystemView('/'.\OCP\User::getUser().'/files_trashbin/files');
|
||||
|
||||
if(!$view->file_exists($filename)) {
|
||||
header("HTTP/1.0 404 Not Found");
|
||||
$tmpl = new OCP\Template( '', '404', 'guest' );
|
||||
$tmpl->assign('file', $filename);
|
||||
$tmpl->printPage();
|
||||
exit;
|
||||
}
|
||||
|
||||
$ftype=$view->getMimeType( $filename );
|
||||
|
||||
header('Content-Type:'.$ftype);if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) {
|
||||
header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' );
|
||||
} else {
|
||||
header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode( basename($filename) )
|
||||
. '; filename="' . rawurlencode( basename($filename) ) . '"' );
|
||||
}
|
||||
OCP\Response::disableCaching();
|
||||
header('Content-Length: '. $view->filesize($filename));
|
||||
|
||||
OC_Util::obEnd();
|
||||
$view->readfile( $filename );
|
||||
@@ -10,6 +10,7 @@ OCP\Util::addScript('files', 'fileactions');
|
||||
$tmpl = new OCP\Template('files_trashbin', 'index', 'user');
|
||||
|
||||
OCP\Util::addStyle('files', 'files');
|
||||
OCP\Util::addStyle('files_trashbin', 'trash');
|
||||
OCP\Util::addScript('files', 'filelist');
|
||||
// filelist overrides
|
||||
OCP\Util::addScript('files_trashbin', 'filelist');
|
||||
|
||||
@@ -3,12 +3,12 @@ $(document).ready(function() {
|
||||
|
||||
if (typeof FileActions !== 'undefined') {
|
||||
FileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/history'), function(filename) {
|
||||
var tr = $('tr').filterAttr('data-file', filename);
|
||||
var deleteAction = $('tr').filterAttr('data-file', filename).children("td.date").children(".action.delete");
|
||||
var tr = FileList.findFileEl(filename);
|
||||
var deleteAction = tr.children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
disableActions();
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'undelete.php'),
|
||||
{files: JSON.stringify([filename]), dirlisting: tr.attr('data-dirlisting')},
|
||||
{files: JSON.stringify([$('#dir').val() + '/' + filename]), dirlisting: tr.attr('data-dirlisting')},
|
||||
function(result) {
|
||||
for (var i = 0; i < result.data.success.length; i++) {
|
||||
var row = document.getElementById(result.data.success[i].filename);
|
||||
@@ -30,12 +30,12 @@ $(document).ready(function() {
|
||||
return OC.imagePath('core', 'actions/delete');
|
||||
}, function(filename) {
|
||||
$('.tipsy').remove();
|
||||
var tr = $('tr').filterAttr('data-file', filename);
|
||||
var deleteAction = $('tr').filterAttr('data-file', filename).children("td.date").children(".action.delete");
|
||||
var tr = FileList.findFileEl(filename);
|
||||
var deleteAction = tr.children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
disableActions();
|
||||
$.post(OC.filePath('files_trashbin', 'ajax', 'delete.php'),
|
||||
{files: JSON.stringify([filename]), dirlisting: tr.attr('data-dirlisting')},
|
||||
{files: JSON.stringify([$('#dir').val() + '/' +filename]), dirlisting: tr.attr('data-dirlisting')},
|
||||
function(result) {
|
||||
for (var i = 0; i < result.data.success.length; i++) {
|
||||
var row = document.getElementById(result.data.success[i].filename);
|
||||
@@ -66,41 +66,6 @@ $(document).ready(function() {
|
||||
procesSelection();
|
||||
});
|
||||
|
||||
$('#fileList').on('click', 'td.filename a', function(event) {
|
||||
if (event.shiftKey) {
|
||||
event.preventDefault();
|
||||
var last = $(lastChecked).parent().parent().prevAll().length;
|
||||
var first = $(this).parent().parent().prevAll().length;
|
||||
var start = Math.min(first, last);
|
||||
var end = Math.max(first, last);
|
||||
var rows = $(this).parent().parent().parent().children('tr');
|
||||
for (var i = start; i < end; i++) {
|
||||
$(rows).each(function(index) {
|
||||
if (index == i) {
|
||||
var checkbox = $(this).children().children('input:checkbox');
|
||||
$(checkbox).attr('checked', 'checked');
|
||||
$(checkbox).parent().parent().addClass('selected');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
var checkbox = $(this).parent().children('input:checkbox');
|
||||
lastChecked = checkbox;
|
||||
if ($(checkbox).attr('checked')) {
|
||||
$(checkbox).removeAttr('checked');
|
||||
$(checkbox).parent().parent().removeClass('selected');
|
||||
$('#select_all').removeAttr('checked');
|
||||
} else {
|
||||
$(checkbox).attr('checked', 'checked');
|
||||
$(checkbox).parent().parent().toggleClass('selected');
|
||||
var selectedCount = $('td.filename input:checkbox:checked').length;
|
||||
if (selectedCount == $('td.filename input:checkbox').length) {
|
||||
$('#select_all').attr('checked', 'checked');
|
||||
}
|
||||
}
|
||||
procesSelection();
|
||||
});
|
||||
|
||||
$('.undelete').click('click', function(event) {
|
||||
event.preventDefault();
|
||||
var files = getSelectedFiles('file');
|
||||
@@ -108,7 +73,7 @@ $(document).ready(function() {
|
||||
var dirlisting = getSelectedFiles('dirlisting')[0];
|
||||
disableActions();
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var deleteAction = $('tr').filterAttr('data-file', files[i]).children("td.date").children(".action.delete");
|
||||
var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
}
|
||||
|
||||
@@ -136,7 +101,8 @@ $(document).ready(function() {
|
||||
var params = {};
|
||||
if (allFiles) {
|
||||
params = {
|
||||
allfiles: true
|
||||
allfiles: true,
|
||||
dir: $('#dir').val()
|
||||
};
|
||||
}
|
||||
else {
|
||||
@@ -153,7 +119,7 @@ $(document).ready(function() {
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var deleteAction = $('tr').filterAttr('data-file', files[i]).children("td.date").children(".action.delete");
|
||||
var deleteAction = FileList.findFileEl(files[i]).children("td.date").children(".action.delete");
|
||||
deleteAction.removeClass('delete-icon').addClass('progress-icon');
|
||||
}
|
||||
}
|
||||
@@ -183,13 +149,27 @@ $(document).ready(function() {
|
||||
|
||||
});
|
||||
|
||||
$('#fileList').on('click', 'td.filename input', function() {
|
||||
var checkbox = $(this).parent().children('input:checkbox');
|
||||
$(checkbox).parent().parent().toggleClass('selected');
|
||||
if ($(checkbox).is(':checked')) {
|
||||
var selectedCount = $('td.filename input:checkbox:checked').length;
|
||||
if (selectedCount === $('td.filename input:checkbox').length) {
|
||||
$('#select_all').prop('checked', true);
|
||||
}
|
||||
} else {
|
||||
$('#select_all').prop('checked',false);
|
||||
}
|
||||
procesSelection();
|
||||
});
|
||||
|
||||
$('#fileList').on('click', 'td.filename a', function(event) {
|
||||
var mime = $(this).parent().parent().data('mime');
|
||||
if (mime !== 'httpd/unix-directory') {
|
||||
event.preventDefault();
|
||||
}
|
||||
var filename = $(this).parent().parent().attr('data-file');
|
||||
var tr = $('tr').filterAttr('data-file',filename);
|
||||
var tr = FileList.findFileEl(filename);
|
||||
var renaming = tr.data('renaming');
|
||||
if(!renaming && !FileList.isLoading(filename)){
|
||||
if(mime.substr(0, 5) === 'text/'){ //no texteditor for now
|
||||
@@ -229,7 +209,7 @@ function getSelectedFiles(property){
|
||||
elements.each(function(i,element){
|
||||
var file={
|
||||
name:$(element).attr('data-filename'),
|
||||
file:$(element).attr('data-file'),
|
||||
file:$('#dir').val() + "/" + $(element).attr('data-file'),
|
||||
timestamp:$(element).attr('data-timestamp'),
|
||||
type:$(element).attr('data-type'),
|
||||
dirlisting:$(element).attr('data-dirlisting')
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?php
|
||||
$TRANSLATIONS = array(
|
||||
"Error" => "Ошибка",
|
||||
"Delete" => "Удалить"
|
||||
);
|
||||
$PLURAL_FORMS = "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);";
|
||||
@@ -44,8 +44,10 @@ class Helper
|
||||
}
|
||||
|
||||
$files = array();
|
||||
$id = 0;
|
||||
foreach ($result as $r) {
|
||||
$i = array();
|
||||
$i['id'] = $id++;
|
||||
$i['name'] = $r['id'];
|
||||
$i['date'] = \OCP\Util::formatDate($r['timestamp']);
|
||||
$i['timestamp'] = $r['timestamp'];
|
||||
|
||||
@@ -189,7 +189,7 @@ class Trashbin {
|
||||
if ($rootView->is_dir($owner . '/files_versions/' . $ownerPath)) {
|
||||
$size += self::calculateSize(new \OC\Files\View('/' . $owner . '/files_versions/' . $ownerPath));
|
||||
if ($owner !== $user) {
|
||||
$rootView->copy($owner . '/files_versions/' . $ownerPath, $owner . '/files_trashbin/versions/' . basename($ownerPath) . '.d' . $timestamp);
|
||||
self::copy_recursive($owner . '/files_versions/' . $ownerPath, $owner . '/files_trashbin/versions/' . basename($ownerPath) . '.d' . $timestamp, $rootView);
|
||||
}
|
||||
$rootView->rename($owner . '/files_versions/' . $ownerPath, $user . '/files_trashbin/versions/' . $filename . '.d' . $timestamp);
|
||||
} else if ($versions = \OCA\Files_Versions\Storage::getVersions($owner, $ownerPath)) {
|
||||
@@ -247,7 +247,7 @@ class Trashbin {
|
||||
if ($rootView->is_dir($keyfile)) {
|
||||
$size += self::calculateSize(new \OC\Files\View($keyfile));
|
||||
if ($owner !== $user) {
|
||||
$rootView->copy($keyfile, $owner . '/files_trashbin/keyfiles/' . basename($ownerPath) . '.d' . $timestamp);
|
||||
self::copy_recursive($keyfile, $owner . '/files_trashbin/keyfiles/' . basename($ownerPath) . '.d' . $timestamp, $rootView);
|
||||
}
|
||||
$rootView->rename($keyfile, $user . '/files_trashbin/keyfiles/' . $filename . '.d' . $timestamp);
|
||||
} else {
|
||||
@@ -265,7 +265,7 @@ class Trashbin {
|
||||
if ($rootView->is_dir($sharekeys)) {
|
||||
$size += self::calculateSize(new \OC\Files\View($sharekeys));
|
||||
if ($owner !== $user) {
|
||||
$rootView->copy($sharekeys, $owner . '/files_trashbin/share-keys/' . basename($ownerPath) . '.d' . $timestamp);
|
||||
self::copy_recursive($sharekeys, $owner . '/files_trashbin/share-keys/' . basename($ownerPath) . '.d' . $timestamp, $rootView);
|
||||
}
|
||||
$rootView->rename($sharekeys, $user . '/files_trashbin/share-keys/' . $filename . '.d' . $timestamp);
|
||||
} else {
|
||||
@@ -564,6 +564,21 @@ class Trashbin {
|
||||
return $size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief delete all files from the trash
|
||||
*/
|
||||
public static function deleteAll() {
|
||||
$user = \OCP\User::getUser();
|
||||
$view = new \OC\Files\View('/' . $user);
|
||||
$view->deleteAll('files_trashbin');
|
||||
self::setTrashbinSize($user, 0);
|
||||
$query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=?');
|
||||
$query->execute(array($user));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief delete file from trash bin permanently
|
||||
*
|
||||
@@ -719,7 +734,7 @@ class Trashbin {
|
||||
// calculate available space for trash bin
|
||||
// subtract size of files and current trash bin size from quota
|
||||
if ($softQuota) {
|
||||
$rootInfo = $view->getFileInfo('/files/');
|
||||
$rootInfo = $view->getFileInfo('/files/', false);
|
||||
$free = $quota - $rootInfo['size']; // remaining free space for user
|
||||
if ($free > 0) {
|
||||
$availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $trashbinSize; // how much space can be used for versions
|
||||
@@ -763,8 +778,13 @@ class Trashbin {
|
||||
*/
|
||||
private static function expire($trashbinSize, $user) {
|
||||
|
||||
// let the admin disable auto expire
|
||||
$autoExpire = \OC_Config::getValue('trashbin_auto_expire', true);
|
||||
if ($autoExpire === false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$user = \OCP\User::getUser();
|
||||
$view = new \OC\Files\View('/' . $user);
|
||||
$availableSpace = self::calculateFreeSpace($trashbinSize);
|
||||
$size = 0;
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<img src="<?php print_unescaped(OCP\image_path('core', 'places/home.svg'));?>" class="svg" />
|
||||
</a>
|
||||
</div>
|
||||
<?php if(count($_["breadcrumb"])):?>
|
||||
<div class="crumb svg"
|
||||
data-dir='/'>
|
||||
<div class="crumb svg"
|
||||
data-dir='/'>
|
||||
<a href="<?php p($_['baseURL']); ?>"><?php p($l->t("Deleted Files")); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<?php if(count($_["breadcrumb"])):?>
|
||||
<?php endif;?>
|
||||
<?php for($i=0; $i<count($_["breadcrumb"]); $i++):
|
||||
$crumb = $_["breadcrumb"][$i];
|
||||
|
||||
@@ -35,18 +35,21 @@
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
>
|
||||
<?php if(!isset($_['readonly']) || !$_['readonly']): ?><input type="checkbox" /><?php endif; ?>
|
||||
<?php if(!isset($_['readonly']) || !$_['readonly']): ?>
|
||||
<input id="select-<?php p($file['id']); ?>" type="checkbox" />
|
||||
<label for="select-<?php p($file['id']); ?>"></label>
|
||||
<?php endif; ?>
|
||||
<?php if($file['type'] === 'dir'): ?>
|
||||
<?php if( $_['dirlisting'] ): ?>
|
||||
<a class="name" href="<?php p($_['baseURL'].'/'.$name); ?>" title="">
|
||||
<a class="name dir" href="<?php p($_['baseURL'].'/'.$name); ?>" title="">
|
||||
<?php else: ?>
|
||||
<a class="name" href="<?php p($_['baseURL'].'/'.$name.'.d'.$file['timestamp']); ?>" title="">
|
||||
<a class="name dir" href="<?php p($_['baseURL'].'/'.$name.'.d'.$file['timestamp']); ?>" title="">
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<?php if( $_['dirlisting'] ): ?>
|
||||
<a class="name" href="<?php p($_['downloadURL'].'/'.$name); ?>" title="">
|
||||
<a class="name file" href="<?php p($_['downloadURL'].'/'.$name); ?>" title="">
|
||||
<?php else: ?>
|
||||
<a class="name" href="<?php p($_['downloadURL'].'/'.$name.'.d'.$file['timestamp']);?>" title="">
|
||||
<a class="name file" href="<?php p($_['downloadURL'].'/'.$name.'.d'.$file['timestamp']);?>" title="">
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<span class="nametext">
|
||||
|
||||
@@ -5,7 +5,8 @@ $source = $_GET['source'];
|
||||
$start = $_GET['start'];
|
||||
list ($uid, $filename) = OCA\Files_Versions\Storage::getUidAndFilename($source);
|
||||
$count = 5; //show the newest revisions
|
||||
if( ($versions = OCA\Files_Versions\Storage::getVersions($uid, $filename)) ) {
|
||||
$versions = OCA\Files_Versions\Storage::getVersions($uid, $filename, $source);
|
||||
if( $versions ) {
|
||||
|
||||
$endReached = false;
|
||||
if (count($versions) <= $start+$count) {
|
||||
|
||||
@@ -12,18 +12,11 @@ if(!\OC_App::isEnabled('files_versions')){
|
||||
}
|
||||
|
||||
$file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : '';
|
||||
$user = array_key_exists('user', $_GET) ? $_GET['user'] : '';
|
||||
$maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : 44;
|
||||
$maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : 44;
|
||||
$version = array_key_exists('version', $_GET) ? $_GET['version'] : '';
|
||||
$scalingUp = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true;
|
||||
|
||||
if($user === '') {
|
||||
\OC_Response::setStatus(400); //400 Bad Request
|
||||
\OC_Log::write('versions-preview', 'No user parameter was passed', \OC_Log::DEBUG);
|
||||
exit;
|
||||
}
|
||||
|
||||
if($file === '' && $version === '') {
|
||||
\OC_Response::setStatus(400); //400 Bad Request
|
||||
\OC_Log::write('versions-preview', 'No file parameter was passed', \OC_Log::DEBUG);
|
||||
@@ -36,7 +29,8 @@ if($maxX === 0 || $maxY === 0) {
|
||||
exit;
|
||||
}
|
||||
|
||||
try{
|
||||
try {
|
||||
list($user, $file) = \OCA\Files_Versions\Storage::getUidAndFilename($file);
|
||||
$preview = new \OC\Preview($user, 'files_versions', $file.'.v'.$version);
|
||||
$mimetype = \OC_Helper::getFileNameMimeType($file);
|
||||
$preview->setMimetype($mimetype);
|
||||
|
||||
@@ -36,12 +36,7 @@ $view = new OC\Files\View('/');
|
||||
$ftype = $view->getMimeType('/'.$uid.'/files/'.$filename);
|
||||
|
||||
header('Content-Type:'.$ftype);
|
||||
if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) {
|
||||
header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' );
|
||||
} else {
|
||||
header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode( basename($filename) )
|
||||
. '; filename="' . rawurlencode( basename($filename) ) . '"' );
|
||||
}
|
||||
OCP\Response::setContentDispositionHeader(basename($filename), 'attachment');
|
||||
OCP\Response::disableCaching();
|
||||
header('Content-Length: '.$view->filesize($versionName));
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ function goToVersionPage(url){
|
||||
function createVersionsDropdown(filename, files) {
|
||||
|
||||
var start = 0;
|
||||
var fileEl;
|
||||
|
||||
var html = '<div id="dropdown" class="drop drop-versions" data-file="'+escapeHTML(files)+'">';
|
||||
html += '<div id="private">';
|
||||
@@ -86,8 +87,9 @@ function createVersionsDropdown(filename, files) {
|
||||
html += '<input type="button" value="'+ t('files_versions', 'More versions...') + '" name="show-more-versions" id="show-more-versions" style="display: none;" />';
|
||||
|
||||
if (filename) {
|
||||
$('tr').filterAttr('data-file',filename).addClass('mouseOver');
|
||||
$(html).appendTo($('tr').filterAttr('data-file',filename).find('td.filename'));
|
||||
fileEl = FileList.findFileEl(filename);
|
||||
fileEl.addClass('mouseOver');
|
||||
$(html).appendTo(fileEl.find('td.filename'));
|
||||
} else {
|
||||
$(html).appendTo($('thead .share'));
|
||||
}
|
||||
@@ -138,7 +140,7 @@ function createVersionsDropdown(filename, files) {
|
||||
|
||||
var preview = '<img class="preview" src="'+revision.preview+'"/>';
|
||||
|
||||
var download ='<a href="' + path + "?file=" + files + '&revision=' + revision.version + '">';
|
||||
var download ='<a href="' + path + "?file=" + encodeURIComponent(files) + '&revision=' + revision.version + '">';
|
||||
download+='<img';
|
||||
download+=' src="' + OC.imagePath('core', 'actions/download') + '"';
|
||||
download+=' name="downloadVersion" />';
|
||||
@@ -146,8 +148,7 @@ function createVersionsDropdown(filename, files) {
|
||||
download+='</a>';
|
||||
|
||||
var revert='<span class="revertVersion"';
|
||||
revert+=' id="' + revision.version + '"';
|
||||
revert+=' value="' + files + '">';
|
||||
revert+=' id="' + revision.version + '">';
|
||||
revert+='<img';
|
||||
revert+=' src="' + OC.imagePath('core', 'actions/history') + '"';
|
||||
revert+=' name="revertVersion"';
|
||||
@@ -156,14 +157,13 @@ function createVersionsDropdown(filename, files) {
|
||||
var version=$('<li/>');
|
||||
version.attr('value', revision.version);
|
||||
version.html(preview + download + revert);
|
||||
// add file here for proper name escaping
|
||||
version.find('span.revertVersion').attr('value', files);
|
||||
|
||||
version.appendTo('#found_versions');
|
||||
}
|
||||
|
||||
$('tr').filterAttr('data-file',filename).addClass('mouseOver');
|
||||
$('#dropdown').show('blind');
|
||||
|
||||
|
||||
}
|
||||
|
||||
$(this).click(
|
||||
|
||||
@@ -98,7 +98,6 @@ class Storage {
|
||||
|
||||
$files_view = new \OC\Files\View('/'.$uid .'/files');
|
||||
$users_view = new \OC\Files\View('/'.$uid);
|
||||
$versions_view = new \OC\Files\View('/'.$uid.'/files_versions');
|
||||
|
||||
// check if filename is a directory
|
||||
if($files_view->is_dir($filename)) {
|
||||
@@ -132,7 +131,10 @@ class Storage {
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
// store a new version of a file
|
||||
$users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename));
|
||||
$mtime = $users_view->filemtime('files'.$filename);
|
||||
$users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'. $mtime);
|
||||
// call getFileInfo to enforce a file cache entry for the new version
|
||||
$users_view->getFileInfo('files_versions'.$filename.'.v'.$mtime);
|
||||
|
||||
// reset proxy state
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
@@ -259,11 +261,12 @@ class Storage {
|
||||
|
||||
/**
|
||||
* @brief get a list of all available versions of a file in descending chronological order
|
||||
* @param $uid user id from the owner of the file
|
||||
* @param $filename file to find versions of, relative to the user files dir
|
||||
* @returns array
|
||||
* @param string $uid user id from the owner of the file
|
||||
* @param string $filename file to find versions of, relative to the user files dir
|
||||
* @param string $userFullPath
|
||||
* @returns array versions newest first
|
||||
*/
|
||||
public static function getVersions($uid, $filename) {
|
||||
public static function getVersions($uid, $filename, $userFullPath = '') {
|
||||
$versions = array();
|
||||
// fetch for old versions
|
||||
$view = new \OC\Files\View('/' . $uid . '/' . self::VERSIONS_ROOT);
|
||||
@@ -284,7 +287,11 @@ class Storage {
|
||||
$versions[$key]['cur'] = 0;
|
||||
$versions[$key]['version'] = $version;
|
||||
$versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($version);
|
||||
$versions[$key]['preview'] = \OCP\Util::linkToRoute('core_ajax_versions_preview', array('file' => $filename, 'version' => $version, 'user' => $uid));
|
||||
if (empty($userFullPath)) {
|
||||
$versions[$key]['preview'] = '';
|
||||
} else {
|
||||
$versions[$key]['preview'] = \OCP\Util::linkToRoute('core_ajax_versions_preview', array('file' => $userFullPath, 'version' => $version));
|
||||
}
|
||||
$versions[$key]['path'] = $filename;
|
||||
$versions[$key]['name'] = $versionedFile;
|
||||
$versions[$key]['size'] = $file['size'];
|
||||
@@ -390,12 +397,13 @@ class Storage {
|
||||
}
|
||||
}
|
||||
|
||||
ksort($versions);
|
||||
// newest first
|
||||
krsort($versions);
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ($versions as $key => $value) {
|
||||
$size = $view->filesize($value['path']);
|
||||
$size = $view->filesize(self::VERSIONS_ROOT.'/'.$value['path'].'.v'.$value['timestamp']);
|
||||
$filename = $value['path'];
|
||||
|
||||
$result['all'][$key]['version'] = $value['timestamp'];
|
||||
@@ -443,7 +451,7 @@ class Storage {
|
||||
// subtract size of files and current versions size from quota
|
||||
if ($softQuota) {
|
||||
$files_view = new \OC\Files\View('/'.$uid.'/files');
|
||||
$rootInfo = $files_view->getFileInfo('/');
|
||||
$rootInfo = $files_view->getFileInfo('/', false);
|
||||
$free = $quota-$rootInfo['size']; // remaining free space for user
|
||||
if ( $free > 0 ) {
|
||||
$availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - ($versionsSize + $offset); // how much space can be used for versions
|
||||
@@ -506,8 +514,8 @@ class Storage {
|
||||
* @brief delete old version from a given list of versions
|
||||
*
|
||||
* @param array $versionsByFile list of versions ordered by files
|
||||
* @param array $allVversions all versions accross multiple files
|
||||
* @param $versionsFileview OC\Files\View on data/user/files_versions
|
||||
* @param array $allVversions all versions across multiple files
|
||||
* @param $versionsFileview \OC\Files\View on data/user/files_versions
|
||||
* @return size of releted versions
|
||||
*/
|
||||
private static function delOldVersions($versionsByFile, &$allVersions, $versionsFileview) {
|
||||
@@ -517,7 +525,6 @@ class Storage {
|
||||
|
||||
// delete old versions for every given file
|
||||
foreach ($versionsByFile as $versions) {
|
||||
$versions = array_reverse($versions); // newest version first
|
||||
|
||||
$interval = 1;
|
||||
$step = Storage::$max_versions_per_interval[$interval]['step'];
|
||||
@@ -536,15 +543,16 @@ class Storage {
|
||||
foreach ($versions as $key => $version) {
|
||||
$newInterval = true;
|
||||
while ($newInterval) {
|
||||
if ($nextInterval == -1 || $version['version'] >= $nextInterval) {
|
||||
if ($nextInterval == -1 || $prevTimestamp > $nextInterval) {
|
||||
if ($version['version'] > $nextVersion) {
|
||||
//distance between two version too small, delete version
|
||||
$versionsFileview->unlink($version['path'] . '.v' . $version['version']);
|
||||
\OC_Hook::emit('\OCP\Versions', 'delete', array('path' => $version['path'] . '.v' . $version['version']));
|
||||
$versionsFileview->unlink($version['path'] . '.v' . $version['version']);
|
||||
$size += $version['size'];
|
||||
unset($allVersions[$key]); // update array with all versions
|
||||
} else {
|
||||
$nextVersion = $version['version'] - $step;
|
||||
$prevTimestamp = $version['version'];
|
||||
}
|
||||
$newInterval = false; // version checked so we can move to the next one
|
||||
} else { // time to move on to the next interval
|
||||
@@ -559,7 +567,6 @@ class Storage {
|
||||
$newInterval = true; // we changed the interval -> check same version with new interval
|
||||
}
|
||||
}
|
||||
$prevTimestamp = $version['version'];
|
||||
}
|
||||
}
|
||||
return $size;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user