Compare commits
772 Commits
fix/sql-tz
...
v6.0.6RC1
| Author | SHA1 | Date | |
|---|---|---|---|
| 784931f835 | |||
| 9327abd2e4 | |||
| 07377be378 | |||
| fd55d73ac5 | |||
| 1aedc60aa2 | |||
| af97529339 | |||
| 3844585f91 | |||
| dfa57cdc01 | |||
| c526ebb832 | |||
| 94d5f9e6e2 | |||
| 65c655afad | |||
| d9ceeed60d | |||
| 5e0ea9f3b5 | |||
| b358547699 | |||
| 34a803d83e | |||
| 716613fda4 | |||
| 3ec5cbb347 | |||
| f2629142b0 | |||
| 5de19f38c4 | |||
| 77a4b1609b | |||
| e001dfb3be | |||
| e123b6db68 | |||
| 7539309742 | |||
| 7217fb873c | |||
| b963723c21 | |||
| 7a2dbc25cb | |||
| 16c5925155 | |||
| 5ecbd9ddd6 | |||
| c3002b3053 | |||
| e962c190c7 | |||
| 725360fa14 | |||
| 69f198d6fb | |||
| 7507995485 | |||
| 4a4f52e7a4 | |||
| 8d786ec456 | |||
| 9c4fe0bc3a | |||
| 25524e03c4 | |||
| da1d4bf4fd | |||
| 1d5a664dc0 | |||
| 4a3e6dd528 | |||
| c2e7c19c49 | |||
| f3d335b7c1 | |||
| 6da05d0ca4 | |||
| c82f158c16 | |||
| f2b072bceb | |||
| ee8ded88b3 | |||
| 3c65a503b2 | |||
| 8f45e8107a | |||
| 30098c34ad | |||
| e895f06165 | |||
| 96430a99f9 | |||
| eb276df08c | |||
| d9ecfa3bd4 | |||
| a5aa04ce19 | |||
| 1e692e56e1 | |||
| 663daf4bc7 | |||
| 8f0d89a986 | |||
| 6d6dfe2329 | |||
| b067848509 | |||
| ca33b2af72 | |||
| f82aed0075 | |||
| 5e85790f5e | |||
| ffe3bd1429 | |||
| 851d478691 | |||
| 74a994c561 | |||
| 534336162e | |||
| 274de5af86 | |||
| 2a34f530b2 | |||
| d046527409 | |||
| e7f9c21fb3 | |||
| e1a77bdcc9 | |||
| 6b38c1de68 | |||
| be07def0af | |||
| 66cccd4e4e | |||
| 2c192a1b76 | |||
| ca29c8bab1 | |||
| f00563c652 | |||
| 3368af82aa | |||
| a66b2f56b9 | |||
| 8d5081f58f | |||
| f99f48e26a | |||
| 6fb60e9d4a | |||
| e8be18a8d8 | |||
| 3a27cc5221 | |||
| a47aad8628 | |||
| 3e2e766f64 | |||
| 51185c1b53 | |||
| 6ef2acb82b | |||
| 304cf0b90c | |||
| add4ce4319 | |||
| 68fa6e6620 | |||
| e4b4c6ce6f | |||
| 82be3674c9 | |||
| c3c87b012a | |||
| 2aabe31cb5 | |||
| afa8006d7c | |||
| c83c1749ce | |||
| 0477d56f5b | |||
| b77298b76d | |||
| d5d55412e1 | |||
| a8ec0a7ccc | |||
| 3c3ebd5cf9 | |||
| fca5af206f | |||
| bfe76fa9e2 | |||
| d9e9191248 | |||
| bbc997aa5e | |||
| 114ce30153 | |||
| 3bec0dc4f1 | |||
| b87591a537 | |||
| 46ade71b5a | |||
| 22cef86b91 | |||
| cf46391f7c | |||
| ff8cd422b5 | |||
| e8167999dc | |||
| e14e4f1dc4 | |||
| 28eabc15bf | |||
| a609d5e031 | |||
| da7641c232 | |||
| 2e264b3a02 | |||
| 9a44862979 | |||
| e4fb6d536b | |||
| 46cc6f5747 | |||
| b66d3632ea | |||
| feca3d0ea9 | |||
| 6eb39f2194 | |||
| 26ded10052 | |||
| 267c0ff567 | |||
| 4b843dfcd1 | |||
| 508d84dcd8 | |||
| 58d04d1772 | |||
| 9caa354cfc | |||
| 0ba9a6b73d | |||
| c491fa272e | |||
| e23e459c41 | |||
| 78feb6514b | |||
| 1c634ab772 | |||
| 53e036e5c0 | |||
| f56a9c08f8 | |||
| 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...1cee06106a
@@ -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)) {
|
||||
|
||||
@@ -82,9 +82,41 @@ if($source) {
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!ini_get('allow_url_fopen')) {
|
||||
$eventSource->send('error', $l10n->t('Server is not allowed to open URLs, please check the server configuration'));
|
||||
$eventSource->close();
|
||||
exit();
|
||||
}
|
||||
|
||||
$ctx = stream_context_create(null, array('notification' =>'progress'));
|
||||
$sourceStream=fopen($source, 'rb', false, $ctx);
|
||||
$result=\OC\Files\Filesystem::file_put_contents($target, $sourceStream);
|
||||
$sourceStream=@fopen($source, 'rb', false, $ctx);
|
||||
$result = 0;
|
||||
if (is_resource($sourceStream)) {
|
||||
$meta = stream_get_meta_data($sourceStream);
|
||||
if (isset($meta['wrapper_data']) && is_array($meta['wrapper_data'])) {
|
||||
//check stream size
|
||||
$storageStats = \OCA\Files\Helper::buildFileStorageStatistics($dir);
|
||||
$freeSpace = $storageStats['freeSpace'];
|
||||
|
||||
foreach($meta['wrapper_data'] as $header) {
|
||||
list($name, $value) = explode(':', $header);
|
||||
if ('content-length' === strtolower(trim($name))) {
|
||||
$length = (int) trim($value);
|
||||
|
||||
if ($length > $freeSpace) {
|
||||
$delta = $length - $freeSpace;
|
||||
$humanDelta = OCP\Util::humanFileSize($delta);
|
||||
|
||||
$eventSource->send('error', (string)$l10n->t('The file exceeds your quota by %s', array($humanDelta)));
|
||||
$eventSource->close();
|
||||
fclose($sourceStream);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$result=\OC\Files\Filesystem::file_put_contents($target, $sourceStream);
|
||||
}
|
||||
if($result) {
|
||||
$meta = \OC\Files\Filesystem::getFileInfo($target);
|
||||
$mime=$meta['mimetype'];
|
||||
@@ -93,6 +125,9 @@ if($source) {
|
||||
} else {
|
||||
$eventSource->send('error', $l10n->t('Error while downloading %s to %s', array($source, $target)));
|
||||
}
|
||||
if (is_resource($sourceStream)) {
|
||||
fclose($sourceStream);
|
||||
}
|
||||
$eventSource->close();
|
||||
exit();
|
||||
} else {
|
||||
|
||||
@@ -19,8 +19,12 @@ if (empty($_POST['dirToken'])) {
|
||||
die();
|
||||
}
|
||||
} else {
|
||||
|
||||
\OC_User::setIncognitoMode(true);
|
||||
|
||||
// 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 +38,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 +48,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 +82,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 +117,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 +150,8 @@ if (strpos($dir, '..') === false) {
|
||||
'originalname' => $files['tmp_name'][$i],
|
||||
'uploadMaxFilesize' => $maxUploadFileSize,
|
||||
'maxHumanFilesize' => $maxHumanFileSize,
|
||||
'permissions' => $meta['permissions'] & $allowedPermissions
|
||||
'permissions' => $meta['permissions'] & $allowedPermissions,
|
||||
'directory' => $directory,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -146,7 +161,7 @@ if (strpos($dir, '..') === false) {
|
||||
} catch(Exception $ex) {
|
||||
$error = $ex->getMessage();
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// file already exists
|
||||
$meta = \OC\Files\Filesystem::getFileInfo($target);
|
||||
@@ -163,7 +178,8 @@ if (strpos($dir, '..') === false) {
|
||||
'originalname' => $files['tmp_name'][$i],
|
||||
'uploadMaxFilesize' => $maxUploadFileSize,
|
||||
'maxHumanFilesize' => $maxHumanFileSize,
|
||||
'permissions' => $meta['permissions'] & $allowedPermissions
|
||||
'permissions' => $meta['permissions'] & $allowedPermissions,
|
||||
'directory' => $directory,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, $defaults->getName())
|
||||
$server->addPlugin(new Sabre_DAV_Locks_Plugin($lockBackend));
|
||||
$server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload
|
||||
$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 {
|
||||
@@ -67,7 +68,11 @@ class Scan extends Command {
|
||||
if (is_object($user)) {
|
||||
$user = $user->getUID();
|
||||
}
|
||||
$this->scanFiles($user, $output);
|
||||
if ($this->userManager->userExists($user)) {
|
||||
$this->scanFiles($user, $output);
|
||||
} else {
|
||||
$output->writeln("<error>Unknown user $user</error>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
if (file.type === '' && file.size === 4096) {
|
||||
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) || file.relativePath) {
|
||||
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
|
||||
@@ -288,6 +296,8 @@ $(document).ready(function() {
|
||||
*/
|
||||
start: function(e) {
|
||||
OC.Upload.log('start', e, null);
|
||||
//hide the tooltip otherwise it covers the progress bar
|
||||
$('#upload').tipsy('hide');
|
||||
},
|
||||
submit: function(e, data) {
|
||||
OC.Upload.rememberUpload(data);
|
||||
@@ -400,7 +410,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 +502,7 @@ $(document).ready(function() {
|
||||
if ($(this).children('p').length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$('#new .error').tipsy('hide');
|
||||
|
||||
$('#new li').each(function(i,element) {
|
||||
@@ -506,7 +516,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 +533,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 +615,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 +657,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;
|
||||
@@ -658,7 +668,11 @@ $(document).ready(function() {
|
||||
});
|
||||
eventSource.listen('error',function(error) {
|
||||
$('#uploadprogressbar').fadeOut();
|
||||
alert(error);
|
||||
OC.Notification.show(error);
|
||||
// hide notification after 10 sec
|
||||
setTimeout(function() {
|
||||
OC.Notification.hide();
|
||||
}, 10000);
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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,7 +0,0 @@
|
||||
<?php
|
||||
$TRANSLATIONS = array(
|
||||
"_%n folder_::_%n folders_" => array("",""),
|
||||
"_%n file_::_%n files_" => array("",""),
|
||||
"_Uploading %n file_::_Uploading %n files_" => array("","")
|
||||
);
|
||||
$PLURAL_FORMS = "nplurals=2; plural=(n != 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);";
|
||||
@@ -15,7 +15,8 @@ class Helper
|
||||
|
||||
return array('uploadMaxFilesize' => $maxUploadFilesize,
|
||||
'maxHumanFilesize' => $maxHumanFilesize,
|
||||
'usedSpacePercent' => (int)$storageInfo['relative']);
|
||||
'usedSpacePercent' => (int)$storageInfo['relative'],
|
||||
'freeSpace' => (int)$storageInfo['free']);
|
||||
}
|
||||
|
||||
public static function determineIcon($file) {
|
||||
|
||||
@@ -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 is enabled you need to re-login to initialize your encryption keys. Please note that server side encryption requires that the ownCloud server admin can be trusted. The main purpose of this app is the encryption of files that are stored on externally mounted storages.</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/>
|
||||
|
||||
@@ -5,6 +5,9 @@ if (!isset($_)) { //also provide standalone error page
|
||||
|
||||
$l = OC_L10N::get('files_encryption');
|
||||
|
||||
OC_JSON::checkAppEnabled('files_encryption');
|
||||
OC_App::loadApp('files_encryption');
|
||||
|
||||
if (isset($_GET['errorCode'])) {
|
||||
$errorCode = $_GET['errorCode'];
|
||||
switch ($errorCode) {
|
||||
|
||||
@@ -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,20 +194,23 @@ class Hooks {
|
||||
// the necessary keys)
|
||||
if (Crypt::mode() === 'server') {
|
||||
|
||||
if ($params['uid'] === \OCP\User::getUser()) {
|
||||
$view = new \OC_FilesystemView('/');
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
|
||||
$view = new \OC_FilesystemView('/');
|
||||
// Get existing decrypted private key
|
||||
$privateKey = $session->getPrivateKey();
|
||||
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
|
||||
// Get existing decrypted private key
|
||||
$privateKey = $session->getPrivateKey();
|
||||
if ($params['uid'] === \OCP\User::getUser() && $privateKey) {
|
||||
|
||||
// Encrypt private key with new user pwd as passphrase
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($privateKey, $params['password']);
|
||||
|
||||
// Save private key
|
||||
Keymanager::setPrivateKey($encryptedPrivateKey);
|
||||
if ($encryptedPrivateKey) {
|
||||
Keymanager::setPrivateKey($encryptedPrivateKey);
|
||||
} else {
|
||||
\OCP\Util::writeLog('files_encryption', 'Could not update users encryption password', \OCP\Util::ERROR);
|
||||
}
|
||||
|
||||
// NOTE: Session does not need to be updated as the
|
||||
// private key has not changed, only the passphrase
|
||||
@@ -202,36 +220,48 @@ 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);
|
||||
// backup old keys
|
||||
$util->backupAllKeys('recovery');
|
||||
|
||||
$keypair = Crypt::createKeypair();
|
||||
$newUserPassword = $params['password'];
|
||||
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
// make sure that the users home is mounted
|
||||
\OC\Files\Filesystem::initMountPoints($user);
|
||||
|
||||
// Save public key
|
||||
$view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']);
|
||||
$keypair = Crypt::createKeypair();
|
||||
|
||||
// Encrypt private key empty passphrase
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword);
|
||||
// Disable encryption proxy to prevent recursive calls
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
// Save private key
|
||||
$view->file_put_contents(
|
||||
'/' . $user . '/files_encryption/' . $user . '.private.key', $encryptedPrivateKey);
|
||||
// Save public key
|
||||
$view->file_put_contents('/public-keys/' . $user . '.public.key', $keypair['publicKey']);
|
||||
|
||||
if ($recoveryPassword) { // if recovery key is set we can re-encrypt the key files
|
||||
$util = new Util($view, $user);
|
||||
$util->recoverUsersFiles($recoveryPassword);
|
||||
// Encrypt private key empty passphrase
|
||||
$encryptedPrivateKey = Crypt::symmetricEncryptFileContent($keypair['privateKey'], $newUserPassword);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,7 +336,6 @@ class Hooks {
|
||||
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
|
||||
|
||||
$view = new \OC_FilesystemView('/');
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
$userId = \OCP\User::getUser();
|
||||
$util = new Util($view, $userId);
|
||||
$path = $util->fileIdToPath($params['itemSource']);
|
||||
@@ -363,25 +392,41 @@ class Hooks {
|
||||
}
|
||||
}
|
||||
|
||||
$sharingEnabled = \OCP\Share::isEnabled();
|
||||
|
||||
// get the path including mount point only if not a shared folder
|
||||
if (strncmp($path, '/Shared', strlen('/Shared') !== 0)) {
|
||||
// get path including the the storage mount point
|
||||
$path = $util->getPathWithMountPoint($params['itemSource']);
|
||||
}
|
||||
|
||||
// if a folder was shared, get a list of all (sub-)folders
|
||||
if ($params['itemType'] === 'folder') {
|
||||
$allFiles = $util->getAllFiles($path);
|
||||
} else {
|
||||
$allFiles = array($path);
|
||||
}
|
||||
self::updateKeyfiles($path, $params['itemType']);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($allFiles as $path) {
|
||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path);
|
||||
$util->setSharedFileKeyfiles($session, $usersSharing, $path);
|
||||
}
|
||||
/**
|
||||
* update keyfiles and share keys recursively
|
||||
*
|
||||
* @param string $path to the file/folder
|
||||
* @param string $type 'file' or 'folder'
|
||||
*/
|
||||
private static function updateKeyfiles($path, $type) {
|
||||
|
||||
$view = new \OC_FilesystemView('/');
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
$userId = \OCP\User::getUser();
|
||||
$util = new Util($view, $userId);
|
||||
|
||||
$sharingEnabled = \OCP\Share::isEnabled();
|
||||
|
||||
// if a folder was shared, get a list of all (sub-)folders
|
||||
if ($type === 'folder') {
|
||||
$allFiles = $util->getAllFiles($path);
|
||||
} else {
|
||||
$allFiles = array($path);
|
||||
}
|
||||
|
||||
foreach ($allFiles as $path) {
|
||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $path);
|
||||
$util->setSharedFileKeyfiles($session, $usersSharing, $path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,13 +520,63 @@ class Hooks {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
* This function is connected to the rename signal of OC_Filesystem and adjust the name and location
|
||||
* of the stored versions along the actual file
|
||||
* @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 postRename($params) {
|
||||
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']);
|
||||
$type = $view->is_dir('/' . $user . '/files/' . $params['oldpath']) ? 'folder' : 'file';
|
||||
if ($mp1 === $mp2) {
|
||||
self::$renamedFiles[$params['oldpath']] = array(
|
||||
'uid' => $ownerOld,
|
||||
'path' => $pathOld,
|
||||
'type' => $type,
|
||||
'operation' => 'rename',
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 preCopy($params) {
|
||||
$user = \OCP\User::getUser();
|
||||
$view = new \OC\Files\View('/');
|
||||
$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']);
|
||||
|
||||
$type = $view->is_dir('/' . $user . '/files/' . $params['oldpath']) ? 'folder' : 'file';
|
||||
|
||||
if ($mp1 === $mp2) {
|
||||
self::$renamedFiles[$params['oldpath']] = array(
|
||||
'uid' => $ownerOld,
|
||||
'path' => $pathOld,
|
||||
'type' => $type,
|
||||
'operation' => 'copy');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* after a file is renamed/copied, rename/copy its keyfile and share-keys also fix the file size and fix also the sharing
|
||||
*
|
||||
* @param array $params array with oldpath and newpath
|
||||
*/
|
||||
public static function postRenameOrCopy($params) {
|
||||
|
||||
if (\OCP\App::isEnabled('files_encryption') === false) {
|
||||
return true;
|
||||
@@ -492,56 +587,69 @@ class Hooks {
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$view = new \OC_FilesystemView('/');
|
||||
$session = new \OCA\Encryption\Session($view);
|
||||
$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'];
|
||||
$type = self::$renamedFiles[$params['oldpath']]['type'];
|
||||
$operation = self::$renamedFiles[$params['oldpath']]['operation'];
|
||||
unset(self::$renamedFiles[$params['oldpath']]);
|
||||
} 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::DEBUG);
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
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
|
||||
if (!$view->is_dir($oldKeyfilePath)) {
|
||||
if ($type === 'file') {
|
||||
$oldKeyfilePath .= '.key';
|
||||
$newKeyfilePath .= '.key';
|
||||
|
||||
// create destination folder if not exists
|
||||
$localKeyPath = $view->getLocalFile($oldShareKeyPath);
|
||||
$newLocalKeyPath = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $localKeyPath));
|
||||
if (!file_exists(dirname($newLocalKeyPath))) {
|
||||
mkdir(dirname($newLocalKeyPath), 0750, true);
|
||||
}
|
||||
|
||||
|
||||
// handle share-keys
|
||||
$localKeyPath = $view->getLocalFile($baseDir . 'share-keys/' . $params['oldpath']);
|
||||
$escapedPath = Helper::escapeGlobPattern($localKeyPath);
|
||||
$matches = glob($escapedPath . '*.shareKey');
|
||||
$matches = Helper::findShareKeys($oldShareKeyPath, $view);
|
||||
foreach ($matches as $src) {
|
||||
$dst = \OC\Files\Filesystem::normalizePath(str_replace($params['oldpath'], $params['newpath'], $src));
|
||||
|
||||
// create destination folder if not exists
|
||||
if (!file_exists(dirname($dst))) {
|
||||
mkdir(dirname($dst), 0750, true);
|
||||
}
|
||||
|
||||
rename($src, $dst);
|
||||
$dst = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $src));
|
||||
$view->$operation($src, $dst);
|
||||
}
|
||||
|
||||
} 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->$operation($oldShareKeyPath, $newShareKeyPath);
|
||||
}
|
||||
|
||||
// Rename keyfile so it isn't orphaned
|
||||
@@ -549,26 +657,19 @@ 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);
|
||||
$view->$operation($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();
|
||||
$util->fixFileSize($newPath);
|
||||
|
||||
// get users
|
||||
$usersSharing = $util->getSharingUsersArray($sharingEnabled, $newPathRelative);
|
||||
|
||||
// update sharing-keys
|
||||
$util->setSharedFileKeyfiles($session, $usersSharing, $newPathRelative);
|
||||
}
|
||||
// update sharing-keys
|
||||
self::updateKeyfiles($params['newpath'], $type);
|
||||
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
}
|
||||
@@ -600,4 +701,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,12 @@ class Helper {
|
||||
*/
|
||||
public static function registerFilesystemHooks() {
|
||||
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Encryption\Hooks', 'preRename');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRenameOrCopy');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'copy', 'OCA\Encryption\Hooks', 'preCopy');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_copy', 'OCA\Encryption\Hooks', 'postRenameOrCopy');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'post_delete', 'OCA\Encryption\Hooks', 'postDelete');
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Encryption\Hooks', 'preDelete');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,7 +281,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 +377,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -409,12 +423,50 @@ class Helper {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief glob uses different pattern than regular expressions, escape glob pattern only
|
||||
* @param unescaped path
|
||||
* @return escaped path
|
||||
* find all share keys for a given file
|
||||
* @param string $path to the file
|
||||
* @param \OC\Files\View $view view, relative to data/
|
||||
* @return array list of files, path relative to data/
|
||||
*/
|
||||
public static function escapeGlobPattern($path) {
|
||||
return preg_replace('/(\*|\?|\[)/', '[$1]', $path);
|
||||
public static function findShareKeys($path, $view) {
|
||||
$result = array();
|
||||
$pathinfo = pathinfo($path);
|
||||
$dirContent = $view->opendir($pathinfo['dirname']);
|
||||
|
||||
if (is_resource($dirContent)) {
|
||||
while (($file = readdir($dirContent)) !== false) {
|
||||
if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
|
||||
if (preg_match("/" . $pathinfo['filename'] . ".(.*).shareKey/", $file)) {
|
||||
$result[] = $pathinfo['dirname'] . '/' . $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dirContent);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
@@ -133,7 +133,7 @@ class Keymanager {
|
||||
$basePath = '/' . $owner . '/files_encryption/keyfiles';
|
||||
}
|
||||
|
||||
$targetPath = self::keySetPreparation($view, $filename, $basePath, $owner);
|
||||
$targetPath = self::keySetPreparation($view, $filename, $basePath);
|
||||
|
||||
if (!$view->is_dir($basePath . '/' . $targetPath)) {
|
||||
|
||||
@@ -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)) {
|
||||
@@ -232,24 +241,23 @@ class Keymanager {
|
||||
}
|
||||
|
||||
$result = false;
|
||||
$fileExists = $view->file_exists('/' . $userId . '/files/' . $trimmed);
|
||||
|
||||
if ($view->is_dir($keyPath)) {
|
||||
|
||||
if ($view->is_dir($keyPath) && !$fileExists) {
|
||||
\OCP\Util::writeLog('files_encryption', 'deleteFileKey: delete file key: ' . $keyPath, \OCP\Util::DEBUG);
|
||||
$result = $view->unlink($keyPath);
|
||||
} elseif ($view->file_exists($keyPath . '.key') && !$fileExists) {
|
||||
\OCP\Util::writeLog('files_encryption', 'deleteFileKey: delete file key: ' . $keyPath, \OCP\Util::DEBUG);
|
||||
$result = $view->unlink($keyPath . '.key');
|
||||
|
||||
} else {
|
||||
if ($view->file_exists($keyPath . '.key')) {
|
||||
|
||||
$result = $view->unlink($keyPath . '.key');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!$result) {
|
||||
|
||||
if ($fileExists) {
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
'Could not delete keyfile; does not exist: "' . $keyPath, \OCP\Util::ERROR);
|
||||
|
||||
'Did not delete the file key, file still exists: ' . '/' . $userId . '/files/' . $trimmed, \OCP\Util::ERROR);
|
||||
} elseif (!$result) {
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
'Could not delete keyfile; does not exist: "' . $keyPath, \OCP\Util::ERROR);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@@ -272,8 +280,9 @@ class Keymanager {
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
if (!$view->file_exists(''))
|
||||
if (!$view->file_exists('')) {
|
||||
$view->mkdir('');
|
||||
}
|
||||
|
||||
$result = $view->file_put_contents($user . '.private.key', $key);
|
||||
|
||||
@@ -331,7 +340,7 @@ class Keymanager {
|
||||
$basePath = '/' . $owner . '/files_encryption/share-keys';
|
||||
}
|
||||
|
||||
$shareKeyPath = self::keySetPreparation($view, $filename, $basePath, $owner);
|
||||
$shareKeyPath = self::keySetPreparation($view, $filename, $basePath);
|
||||
|
||||
$result = true;
|
||||
|
||||
@@ -402,7 +411,21 @@ 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 ($view->file_exists('/' . $userId . '/files/' . $filePath)) {
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
'File still exists, stop deleting share keys!', \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -412,21 +435,24 @@ class Keymanager {
|
||||
$baseDir = $userId . '/files_encryption/share-keys/';
|
||||
}
|
||||
|
||||
$result = true;
|
||||
|
||||
if ($view->is_dir($userId . '/files/' . $filePath)) {
|
||||
$view->unlink($baseDir . $filePath);
|
||||
if ($view->is_dir($baseDir . $filePath)) {
|
||||
\OCP\Util::writeLog('files_encryption', 'delAllShareKeys: delete share keys: ' . $baseDir . $filePath, \OCP\Util::DEBUG);
|
||||
$result = $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) {
|
||||
\OCP\Util::writeLog('files_encryption', 'dellAllShareKeys: delete share keys: ' . '/' . $userId . '/' . $path, \OCP\Util::DEBUG);
|
||||
$result &= $view->unlink('/' . $userId . '/' . $path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (bool)$result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -451,19 +477,23 @@ class Keymanager {
|
||||
|
||||
if ($view->is_dir($shareKeyPath)) {
|
||||
|
||||
$localPath = \OC\Files\Filesystem::normalizePath($view->getLocalFolder($shareKeyPath));
|
||||
self::recursiveDelShareKeys($localPath, $userIds);
|
||||
self::recursiveDelShareKeys($shareKeyPath, $userIds, $owner, $view);
|
||||
|
||||
} else {
|
||||
|
||||
foreach ($userIds as $userId) {
|
||||
|
||||
if (!$view->unlink($shareKeyPath . '.' . $userId . '.shareKey')) {
|
||||
if ($userId === $owner && $view->file_exists('/' . $owner . '/files/' . $filename)) {
|
||||
\OCP\Util::writeLog('files_encryption', 'Tried to delete owner key, but the file still exists!', \OCP\Util::FATAL);
|
||||
continue;
|
||||
}
|
||||
$result = $view->unlink($shareKeyPath . '.' . $userId . '.shareKey');
|
||||
\OCP\Util::writeLog('files_encryption', 'delShareKey: delete share key: ' . $shareKeyPath . '.' . $userId . '.shareKey' , \OCP\Util::DEBUG);
|
||||
if (!$result) {
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
'Could not delete shareKey; does not exist: "' . $shareKeyPath . '.' . $userId
|
||||
. '.shareKey"', \OCP\Util::ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,31 +505,44 @@ class Keymanager {
|
||||
*
|
||||
* @param string $dir directory
|
||||
* @param array $userIds user ids for which the share keys should be deleted
|
||||
* @param string $owner owner of the file
|
||||
* @param \OC\Files\View $view view relative to data/
|
||||
*/
|
||||
private static function recursiveDelShareKeys($dir, $userIds) {
|
||||
foreach ($userIds as $userId) {
|
||||
$extension = '.' . $userId . '.shareKey';
|
||||
$escapedDir = Helper::escapeGlobPattern($dir);
|
||||
$escapedExtension = Helper::escapeGlobPattern($extension);
|
||||
$matches = glob($escapedDir . '/*' . $escapedExtension);
|
||||
}
|
||||
/** @var $matches array */
|
||||
foreach ($matches as $ma) {
|
||||
if (!unlink($ma)) {
|
||||
\OCP\Util::writeLog('Encryption library',
|
||||
'Could not delete shareKey; does not exist: "' . $ma . '"', \OCP\Util::ERROR);
|
||||
private static function recursiveDelShareKeys($dir, $userIds, $owner, $view) {
|
||||
|
||||
$dirContent = $view->opendir($dir);
|
||||
$dirSlices = explode('/', ltrim($dir, '/'));
|
||||
$realFileDir = '/' . $owner . '/files/' . implode('/', array_slice($dirSlices, 3)) . '/';
|
||||
|
||||
if (is_resource($dirContent)) {
|
||||
while (($file = readdir($dirContent)) !== false) {
|
||||
if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
|
||||
if ($view->is_dir($dir . '/' . $file)) {
|
||||
self::recursiveDelShareKeys($dir . '/' . $file, $userIds, $owner, $view);
|
||||
} else {
|
||||
$realFile = $realFileDir . self::getFilenameFromShareKey($file);
|
||||
foreach ($userIds as $userId) {
|
||||
if (preg_match("/(.*)." . $userId . ".shareKey/", $file)) {
|
||||
if ($userId === $owner &&
|
||||
$view->file_exists($realFile)) {
|
||||
\OCP\Util::writeLog('files_encryption', 'original file still exists, keep owners share key!', \OCP\Util::ERROR);
|
||||
continue;
|
||||
}
|
||||
\OCP\Util::writeLog('files_encryption', 'recursiveDelShareKey: delete share key: ' . $file, \OCP\Util::DEBUG);
|
||||
$view->unlink($dir . '/' . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$subdirs = $directories = glob($escapedDir . '/*', GLOB_ONLYDIR);
|
||||
foreach ($subdirs as $subdir) {
|
||||
self::recursiveDelShareKeys($subdir, $userIds);
|
||||
closedir($dirContent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make preparations to vars and filesystem for saving a keyfile
|
||||
*/
|
||||
public static function keySetPreparation(\OC_FilesystemView $view, $path, $basePath, $userId) {
|
||||
public static function keySetPreparation(\OC_FilesystemView $view, $path, $basePath) {
|
||||
|
||||
$targetPath = ltrim($path, '/');
|
||||
|
||||
@@ -510,7 +553,7 @@ class Keymanager {
|
||||
isset($path_parts['dirname'])
|
||||
&& !$view->file_exists($basePath . '/' . $path_parts['dirname'])
|
||||
) {
|
||||
$sub_dirs = explode(DIRECTORY_SEPARATOR, $basePath . '/' . $path_parts['dirname']);
|
||||
$sub_dirs = explode('/', $basePath . '/' . $path_parts['dirname']);
|
||||
$dir = '';
|
||||
foreach ($sub_dirs as $sub_dir) {
|
||||
$dir .= '/' . $sub_dir;
|
||||
@@ -523,4 +566,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
|
||||
|
||||
+264
-154
@@ -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'));
|
||||
}
|
||||
}
|
||||
@@ -1090,6 +1141,10 @@ class Util {
|
||||
// Re-enc keyfile to (additional) sharekeys
|
||||
$multiEncKey = Crypt::multiKeyEncrypt($plainKeyfile, $userPubKeys);
|
||||
|
||||
if ($multiEncKey === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save the recrypted key to it's owner's keyfiles directory
|
||||
// Save new sharekeys to all necessary user directory
|
||||
if (
|
||||
@@ -1113,8 +1168,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 +1188,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,17 +1211,12 @@ 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();
|
||||
foreach ($mount as $mountPoint => $data) {
|
||||
if ($mountPoint == substr($ownerPath, 1, strlen($mountPoint))) {
|
||||
$userIds = array_merge($userIds, $this->getUserWithAccessToMountPoint($data['applicable']['users'], $data['applicable']['groups']));
|
||||
$mounts = \OC_Mount_Config::getSystemMountPoints();
|
||||
foreach ($mounts as $mount) {
|
||||
if ($mount['mountpoint'] == substr($ownerPath, 1, strlen($mount['mountpoint']))) {
|
||||
$userIds = array_merge($userIds, $this->getUserWithAccessToMountPoint($mount['applicable']['users'], $mount['applicable']['groups']));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1188,27 +1242,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 +1292,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 +1323,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 +1433,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 +1478,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 +1502,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 +1521,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 +1540,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'];
|
||||
|
||||
@@ -1738,14 +1770,61 @@ class Util {
|
||||
/**
|
||||
* @brief check if the file is stored on a system wide mount point
|
||||
* @param $path relative to /data/user with leading '/'
|
||||
* create a backup of all keys from the user
|
||||
*
|
||||
* @param string $purpose (optional) define the purpose of the backup, will be part of the backup folder
|
||||
*/
|
||||
public function backupAllKeys($purpose = '') {
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
$backupDir = $this->encryptionDir . '/backup.';
|
||||
$backupDir .= ($purpose === '') ? date("Y-m-d_H-i-s") . '/' : $purpose . '.' . date("Y-m-d_H-i-s") . '/';
|
||||
$this->view->mkdir($backupDir);
|
||||
$this->copyRecursive($this->shareKeysPath, $backupDir . 'share-keys/');
|
||||
$this->copyRecursive($this->keyfilesPath, $backupDir . 'keyfiles/');
|
||||
$this->view->copy($this->privateKeyPath, $backupDir . $this->userId . '.private.key');
|
||||
$this->view->copy($this->publicKeyPath, $backupDir . $this->userId . '.public.key');
|
||||
|
||||
\OC_FileProxy::$enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* helper method to copy a folder recursively, only needed in OC6.
|
||||
* OC7 filesystem and newer can copy folder structures
|
||||
*
|
||||
* @param string $source
|
||||
* @param string $target
|
||||
*/
|
||||
private function copyRecursive($source, $target) {
|
||||
if ($this->view->is_dir($source)) {
|
||||
$this->view->mkdir($target);
|
||||
$dir = $this->view->opendir($source);
|
||||
while ($file = readdir($dir)) {
|
||||
if(!\OC\Files\Filesystem::isIgnoredDir($file)) {
|
||||
$this->copyRecursive($source . '/' . $file, $target . '/' . $file);
|
||||
}
|
||||
}
|
||||
closedir($dir);
|
||||
} else {
|
||||
$this->view->copy($source, $target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* check if the file is stored on a system wide mount point
|
||||
* @param string $path relative to /data/user with leading '/'
|
||||
* @return boolean
|
||||
*/
|
||||
public function isSystemWideMountPoint($path) {
|
||||
$normalizedPath = ltrim($path, '/');
|
||||
if (\OCP\App::isEnabled("files_external")) {
|
||||
$mount = \OC_Mount_Config::getSystemMountPoints();
|
||||
foreach ($mount as $mountPoint => $data) {
|
||||
if ($mountPoint == substr($path, 1, strlen($mountPoint))) {
|
||||
return true;
|
||||
$mounts = \OC_Mount_Config::getSystemMountPoints();
|
||||
foreach ($mounts as $mount) {
|
||||
if ($mount['mountpoint'] == substr($normalizedPath, 0, strlen($mount['mountpoint']))) {
|
||||
if ($this->isMountPointApplicableToUser($mount)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1753,7 +1832,30 @@ class Util {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief decrypt private key and add it to the current session
|
||||
* check if mount point is applicable to user
|
||||
*
|
||||
* @param array $mount contains $mount['applicable']['users'], $mount['applicable']['groups']
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isMountPointApplicableToUser($mount) {
|
||||
$uid = \OCP\User::getUser();
|
||||
$acceptedUids = array('all', $uid);
|
||||
// check if mount point is applicable for the user
|
||||
$intersection = array_intersect($acceptedUids, $mount['applicable']['users']);
|
||||
if (!empty($intersection)) {
|
||||
return true;
|
||||
}
|
||||
// check if mount point is applicable for group where the user is a member
|
||||
foreach ($mount['applicable']['groups'] as $gid) {
|
||||
if (\OC_Group::inGroup($uid, $gid)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt private key and add it to the current session
|
||||
* @param array $params with 'uid' and 'password'
|
||||
* @return mixed session or false
|
||||
*/
|
||||
@@ -1780,4 +1882,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">
|
||||
|
||||
@@ -46,13 +46,12 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
\OC_User::clearBackends();
|
||||
\OC_User::useBackend('database');
|
||||
|
||||
// Filesystem related hooks
|
||||
// clear hooks and register encryption hooks
|
||||
\OC_Hook::clear();
|
||||
\OCA\Encryption\Helper::registerFilesystemHooks();
|
||||
|
||||
// Filesystem related hooks
|
||||
\OCA\Encryption\Helper::registerUserHooks();
|
||||
|
||||
// clear and register hooks
|
||||
// clear and register proxies
|
||||
\OC_FileProxy::clearProxies();
|
||||
\OC_FileProxy::register(new OCA\Encryption\Proxy());
|
||||
|
||||
@@ -95,6 +94,8 @@ class Test_Encryption_Crypt extends \PHPUnit_Framework_TestCase {
|
||||
} else {
|
||||
OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
$this->assertTrue(\OC_FileProxy::$enabled);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
@@ -155,7 +156,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 +215,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 +298,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 +328,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 +419,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 +432,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 +450,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 +463,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 +485,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 +501,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 +520,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 +558,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 +591,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 +615,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 +639,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,421 @@
|
||||
<?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 to the sub-subfolder
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* test rename operation
|
||||
*/
|
||||
function testCopyHook() {
|
||||
|
||||
// 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));
|
||||
|
||||
// copy the file to the sub-subfolder
|
||||
\OC\Files\Filesystem::copy($this->filename, '/' . $this->folder . '/' . $this->folder . '/' . $this->filename);
|
||||
|
||||
$this->assertTrue($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 copied too
|
||||
$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'));
|
||||
|
||||
$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);
|
||||
$this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
public $userId;
|
||||
public $pass;
|
||||
public $stateFilesTrashbin;
|
||||
public static $stateFilesTrashbin;
|
||||
/**
|
||||
* @var OC_FilesystemView
|
||||
*/
|
||||
@@ -50,6 +50,12 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
// disable file proxy by default
|
||||
\OC_FileProxy::$enabled = false;
|
||||
|
||||
// remember files_trashbin state
|
||||
self::$stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
|
||||
|
||||
// we don't want to tests with app files_trashbin enabled
|
||||
\OC_App::disable('files_trashbin');
|
||||
|
||||
// create test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Keymanager::TEST_USER);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Keymanager::TEST_USER, true);
|
||||
@@ -70,28 +76,17 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
$this->view = new \OC_FilesystemView('/');
|
||||
|
||||
\OC_User::setUserId(\Test_Encryption_Keymanager::TEST_USER);
|
||||
\Test_Encryption_Util::loginHelper(Test_Encryption_Keymanager::TEST_USER);
|
||||
$this->userId = \Test_Encryption_Keymanager::TEST_USER;
|
||||
$this->pass = \Test_Encryption_Keymanager::TEST_USER;
|
||||
|
||||
$userHome = \OC_User::getHome($this->userId);
|
||||
$this->dataDir = str_replace('/' . $this->userId, '', $userHome);
|
||||
|
||||
// remember files_trashbin state
|
||||
$this->stateFilesTrashbin = OC_App::isEnabled('files_trashbin');
|
||||
|
||||
// we don't want to tests with app files_trashbin enabled
|
||||
\OC_App::disable('files_trashbin');
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
// reset app files_trashbin
|
||||
if ($this->stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
}
|
||||
else {
|
||||
OC_App::disable('files_trashbin');
|
||||
}
|
||||
$this->view->deleteAll('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys');
|
||||
$this->view->deleteAll('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles');
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
@@ -99,6 +94,10 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Keymanager::TEST_USER);
|
||||
// reset app files_trashbin
|
||||
if (self::$stateFilesTrashbin) {
|
||||
OC_App::enable('files_trashbin');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,6 +135,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 +153,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);
|
||||
|
||||
@@ -193,44 +203,228 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testRecursiveDelShareKeys() {
|
||||
function testRecursiveDelShareKeysFolder() {
|
||||
|
||||
// generate filename
|
||||
$filename = '/tmp-' . time() . '.txt';
|
||||
|
||||
// create folder structure
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/subfolder');
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/subfolder/subsubfolder');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/existingFile.txt', 'data');
|
||||
|
||||
// enable encryption proxy
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = true;
|
||||
// create folder structure for some dummy share key files
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1');
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder');
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder');
|
||||
|
||||
// save file with content
|
||||
$cryptedFile = file_put_contents('crypt:///'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/subfolder/subsubfolder' . $filename, $this->dataShort);
|
||||
// create some dummy share keys
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user1.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.user1.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file2.user2.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file2.user3.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/file2.user3.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file1.user1.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file2.user2.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file2.user3.shareKey', 'data');
|
||||
|
||||
// test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
// recursive delete share keys from user1 and user2
|
||||
Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2', Test_Encryption_Keymanager::TEST_USER), '/folder1/');
|
||||
|
||||
// change encryption proxy to previous state
|
||||
\OC_FileProxy::$enabled = $proxyStatus;
|
||||
|
||||
// recursive delete keys
|
||||
Encryption\Keymanager::delShareKey($this->view, array('admin'), '/folder1/');
|
||||
|
||||
// check if share key not exists
|
||||
// check if share keys from user1 and user2 are deleted
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/admin/files_encryption/share-keys/folder1/subfolder/subsubfolder/' . $filename . '.admin.shareKey'));
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.user1.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.user1.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file2.user2.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file1.user1.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file2.user2.shareKey'));
|
||||
|
||||
// enable encryption proxy
|
||||
$proxyStatus = \OC_FileProxy::$enabled;
|
||||
\OC_FileProxy::$enabled = true;
|
||||
// check if share keys from user3 still exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file2.user3.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file2.user3.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/file2.user3.shareKey'));
|
||||
|
||||
// owner key from existing file should still exists because the file is still there
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey'));
|
||||
|
||||
// cleanup
|
||||
$this->view->unlink('/admin/files/folder1');
|
||||
$this->view->deleteAll('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testRecursiveDelShareKeysFile() {
|
||||
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/existingFile.txt', 'data');
|
||||
|
||||
// create folder structure for some dummy share key files
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1');
|
||||
|
||||
// create some dummy share keys
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user1.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user2.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user3.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey', 'data');
|
||||
|
||||
// recursive delete share keys from user1 and user2
|
||||
Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2', Test_Encryption_Keymanager::TEST_USER), '/folder1/existingFile.txt');
|
||||
|
||||
// check if share keys from user1 and user2 are deleted
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.user1.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.user2.shareKey'));
|
||||
|
||||
// check if share keys for user3 and owner
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user3.shareKey'));
|
||||
// cleanup
|
||||
$this->view->deleteAll('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testDeleteFileKey() {
|
||||
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/existingFile.txt', 'data');
|
||||
|
||||
// create folder structure for some dummy file key files
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles/folder1');
|
||||
|
||||
// create dummy keyfile
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles/folder1/dummyFile.txt.key', 'data');
|
||||
|
||||
// recursive delete share keys from user1 and user2
|
||||
$result = Encryption\Keymanager::deleteFileKey($this->view, '/folder1/existingFile.txt');
|
||||
$this->assertFalse($result);
|
||||
|
||||
$result2 = Encryption\Keymanager::deleteFileKey($this->view, '/folder1/dummyFile.txt');
|
||||
$this->assertTrue($result2);
|
||||
|
||||
// check if file key from dummyFile was deleted
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles/folder1/dummyFile.txt.key'));
|
||||
|
||||
// check if file key from existing file still exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles/folder1/existingFile.txt.key'));
|
||||
|
||||
// cleanup
|
||||
$this->view->deleteAll('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @medium
|
||||
*/
|
||||
function testDeleteFileKeyFolder() {
|
||||
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/existingFile.txt', 'data');
|
||||
|
||||
// create folder structure for some dummy file key files
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles/folder1');
|
||||
|
||||
// create dummy keyfile
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles/folder1/dummyFile.txt.key', 'data');
|
||||
|
||||
// recursive delete share keys from user1 and user2
|
||||
$result = Encryption\Keymanager::deleteFileKey($this->view, '/folder1');
|
||||
$this->assertFalse($result);
|
||||
|
||||
// all file keys should still exists if we try to delete a folder with keys for which some files still exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles/folder1/dummyFile.txt.key'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles/folder1/existingFile.txt.key'));
|
||||
|
||||
// delete folder
|
||||
$this->view->unlink('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
// create dummy keyfile
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles/folder1/dummyFile.txt.key', 'data');
|
||||
|
||||
// now file keys should be deleted since the folder no longer exists
|
||||
$result = Encryption\Keymanager::deleteFileKey($this->view, '/folder1');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/keyfiles/folder1'));
|
||||
|
||||
// cleanup
|
||||
$this->view->deleteAll('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
|
||||
}
|
||||
|
||||
function testDelAllShareKeysFile() {
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/existingFile.txt', 'data');
|
||||
|
||||
// create folder structure for some dummy share key files
|
||||
$this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1');
|
||||
|
||||
// create some dummy share keys for the existing file
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user1.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user2.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user3.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey', 'data');
|
||||
|
||||
// create some dummy share keys for a non-existing file
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.user1.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.user2.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.user3.shareKey', 'data');
|
||||
$this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey', 'data');
|
||||
|
||||
// try to del all share keys from a existing file, should fail because the file still exists
|
||||
$result = Encryption\Keymanager::delAllShareKeys($this->view, Test_Encryption_Keymanager::TEST_USER, 'folder1/existingFile.txt');
|
||||
$this->assertFalse($result);
|
||||
|
||||
// check if share keys still exists
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user1.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user2.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/existingFile.txt.user3.shareKey'));
|
||||
|
||||
// try to del all share keys froma file, should fail because the file still exists
|
||||
$result2 = Encryption\Keymanager::delAllShareKeys($this->view, Test_Encryption_Keymanager::TEST_USER, 'folder1/nonexistingFile.txt');
|
||||
$this->assertTrue($result2);
|
||||
|
||||
// check if share keys are really gone
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.' . Test_Encryption_Keymanager::TEST_USER . '.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.user1.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.user2.shareKey'));
|
||||
$this->assertFalse($this->view->file_exists(
|
||||
'/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/nonexistingFile.txt.user3.shareKey'));
|
||||
|
||||
// cleanup
|
||||
$this->view->deleteAll('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1');
|
||||
|
||||
// change encryption proxy to previous state
|
||||
\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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase {
|
||||
\OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes');
|
||||
|
||||
// clear share hooks
|
||||
\OC_Hook::clear('OCP\\Share');
|
||||
\OC_Hook::clear();
|
||||
\OC::registerShareHooks();
|
||||
\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
|
||||
|
||||
@@ -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,121 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* test if additional share keys are added if we move a folder to a shared parent
|
||||
* @medium
|
||||
*/
|
||||
function testMoveFolder() {
|
||||
|
||||
// login as admin
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
$view = new \OC\Files\View('/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1);
|
||||
|
||||
$filename = '/tmp-' . uniqid();
|
||||
$folder = '/folder' . uniqid();
|
||||
|
||||
\OC\Files\Filesystem::mkdir($folder);
|
||||
|
||||
// Save long data as encrypted file using stream wrapper
|
||||
$cryptedFile = \OC\Files\Filesystem::file_put_contents($folder . $filename, $this->dataShort);
|
||||
|
||||
// Test that data was successfully written
|
||||
$this->assertTrue(is_int($cryptedFile));
|
||||
|
||||
// Get file decrypted contents
|
||||
$decrypt = \OC\Files\Filesystem::file_get_contents($folder . $filename);
|
||||
|
||||
$this->assertEquals($this->dataShort, $decrypt);
|
||||
|
||||
$newFolder = '/newfolder/subfolder' . uniqid();
|
||||
\OC\Files\Filesystem::mkdir('/newfolder');
|
||||
|
||||
// get the file info from previous created file
|
||||
$fileInfo = \OC\Files\Filesystem::getFileInfo('/newfolder');
|
||||
$this->assertTrue(is_array($fileInfo));
|
||||
|
||||
// share the folder
|
||||
\OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL);
|
||||
|
||||
\OC\Files\Filesystem::rename($folder, $newFolder);
|
||||
|
||||
// Get file decrypted contents
|
||||
$newDecrypt = \OC\Files\Filesystem::file_get_contents($newFolder . $filename);
|
||||
$this->assertEquals($this->dataShort, $newDecrypt);
|
||||
|
||||
// check if additional share key for user2 exists
|
||||
$this->assertTrue($view->file_exists('files_encryption/share-keys' . $newFolder . '/' . $filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '.shareKey'));
|
||||
|
||||
// tear down
|
||||
\OC\Files\Filesystem::unlink($newFolder);
|
||||
\OC\Files\Filesystem::unlink('/newfolder');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -22,6 +22,9 @@ use OCA\Encryption;
|
||||
class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
const TEST_ENCRYPTION_UTIL_USER1 = "test-util-user1";
|
||||
const TEST_ENCRYPTION_UTIL_USER2 = "test-util-user2";
|
||||
const TEST_ENCRYPTION_UTIL_GROUP1 = "test-util-group1";
|
||||
const TEST_ENCRYPTION_UTIL_GROUP2 = "test-util-group2";
|
||||
const TEST_ENCRYPTION_UTIL_LEGACY_USER = "test-legacy-user";
|
||||
|
||||
public $userId;
|
||||
@@ -59,11 +62,21 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
|
||||
// create test user
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1, true);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER2, true);
|
||||
\Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER, true);
|
||||
|
||||
// create groups
|
||||
\OC_Group::createGroup(self::TEST_ENCRYPTION_UTIL_GROUP1);
|
||||
\OC_Group::createGroup(self::TEST_ENCRYPTION_UTIL_GROUP2);
|
||||
|
||||
// add user 1 to group1
|
||||
\OC_Group::addToGroup(self::TEST_ENCRYPTION_UTIL_USER1, self::TEST_ENCRYPTION_UTIL_GROUP1);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
@@ -114,7 +127,11 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
public static function tearDownAfterClass() {
|
||||
// cleanup test user
|
||||
\OC_User::deleteUser(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1);
|
||||
\OC_User::deleteUser(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER2);
|
||||
\OC_User::deleteUser(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_LEGACY_USER);
|
||||
//cleanup groups
|
||||
\OC_Group::deleteGroup(self::TEST_ENCRYPTION_UTIL_GROUP1);
|
||||
\OC_Group::deleteGroup(self::TEST_ENCRYPTION_UTIL_GROUP2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,6 +149,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 +271,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 +299,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 +335,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 +367,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 +375,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 +389,133 @@ 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);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* test if all keys get moved to the backup folder correctly
|
||||
*/
|
||||
function testBackupAllKeys() {
|
||||
self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
|
||||
// create some dummy key files
|
||||
$encPath = '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '/files_encryption';
|
||||
$this->view->file_put_contents($encPath . '/keyfiles/foo.key', 'key');
|
||||
$this->view->file_put_contents($encPath . '/share-keys/foo.user1.shareKey', 'share key');
|
||||
$this->view->mkdir($encPath . '/keyfiles/subfolder/');
|
||||
$this->view->mkdir($encPath . '/share-keys/subfolder/');
|
||||
$this->view->file_put_contents($encPath . '/keyfiles/subfolder/foo.key', 'key');
|
||||
$this->view->file_put_contents($encPath . '/share-keys/subfolder/foo.user1.shareKey', 'share key');
|
||||
|
||||
|
||||
$util = new \OCA\Encryption\Util($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
|
||||
$util->backupAllKeys('testing');
|
||||
|
||||
$encFolderContent = $this->view->getDirectoryContent($encPath);
|
||||
|
||||
$backupPath = '';
|
||||
foreach ($encFolderContent as $c) {
|
||||
$name = $c['name'];
|
||||
if (substr($name, 0, strlen('backup')) === 'backup') {
|
||||
$backupPath = $encPath . '/'. $c['name'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertTrue($backupPath !== '');
|
||||
|
||||
// check backupDir Content
|
||||
$this->assertTrue($this->view->is_dir($backupPath . '/keyfiles'));
|
||||
$this->assertTrue($this->view->is_dir($backupPath . '/share-keys'));
|
||||
$this->assertTrue($this->view->file_exists($backupPath . '/keyfiles/foo.key'));
|
||||
$this->assertTrue($this->view->file_exists($backupPath . '/share-keys/foo.user1.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists($backupPath . '/keyfiles/subfolder/foo.key'));
|
||||
$this->assertTrue($this->view->file_exists($backupPath . '/share-keys/subfolder/foo.user1.shareKey'));
|
||||
$this->assertTrue($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.private.key'));
|
||||
$this->assertTrue($this->view->file_exists($backupPath . '/' . self::TEST_ENCRYPTION_UTIL_USER1 . '.public.key'));
|
||||
|
||||
//cleanup
|
||||
$this->view->deleteAll($backupPath);
|
||||
$this->view->unlink($encPath . '/keyfiles/foo.key', 'key');
|
||||
$this->view->unlink($encPath . '/share-keys/foo.user1.shareKey', 'share key');
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
*/
|
||||
@@ -392,7 +569,30 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $user
|
||||
* @dataProvider dataProviderFortestIsMountPointApplicableToUser
|
||||
*/
|
||||
function testIsMountPointApplicableToUser($mount, $expectedResult) {
|
||||
self::loginHelper(self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
$dummyClass = new DummyUtilClass($this->view, self::TEST_ENCRYPTION_UTIL_USER1);
|
||||
$result = $dummyClass->testIsMountPointApplicableToUser($mount);
|
||||
|
||||
$this->assertSame($expectedResult, $result);
|
||||
}
|
||||
|
||||
function dataProviderFortestIsMountPointApplicableToUser() {
|
||||
return array(
|
||||
array(array('applicable' => array('groups' => array(), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER1))), true),
|
||||
array(array('applicable' => array('groups' => array(), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), false),
|
||||
array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP1), 'users' => array())), true),
|
||||
array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP1), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), true),
|
||||
array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2))), false),
|
||||
array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array(self::TEST_ENCRYPTION_UTIL_USER2, 'all'))), true),
|
||||
array(array('applicable' => array('groups' => array(self::TEST_ENCRYPTION_UTIL_GROUP2), 'users' => array('all'))), true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param bool $create
|
||||
* @param bool $password
|
||||
*/
|
||||
@@ -416,6 +616,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
|
||||
@@ -440,3 +646,12 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* dummy class extends \OCA\Encryption\Util to access protected methods for testing
|
||||
*/
|
||||
class DummyUtilClass extends \OCA\Encryption\Util {
|
||||
public function testIsMountPointApplicableToUser($mount) {
|
||||
return $this->isMountPointApplicableToUser($mount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);";
|
||||
@@ -69,6 +69,12 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
sleep($this->timeout);
|
||||
}
|
||||
}
|
||||
private function cleanKey($path) {
|
||||
if ($path === '.') {
|
||||
return '/';
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
public function __construct($params) {
|
||||
if (!isset($params['key']) || !isset($params['secret']) || !isset($params['bucket'])) {
|
||||
@@ -81,9 +87,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'].'/';
|
||||
@@ -115,11 +121,10 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
throw new \Exception("Creation of bucket failed.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->file_exists('.')) {
|
||||
$result = $this->connection->putObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => '.',
|
||||
'Key' => $this->cleanKey('.'),
|
||||
'Body' => '',
|
||||
'ContentType' => 'httpd/unix-directory',
|
||||
'ContentLength' => 0
|
||||
@@ -164,7 +169,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
try {
|
||||
$result = $this->connection->doesObjectExist(
|
||||
$this->bucket,
|
||||
$path
|
||||
$this->cleanKey($path)
|
||||
);
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
@@ -258,7 +263,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
|
||||
$result = $this->connection->headObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path
|
||||
'Key' => $this->cleanKey($path)
|
||||
));
|
||||
|
||||
$stat = array();
|
||||
@@ -288,8 +293,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
if ($path != '.') {
|
||||
$path .= '/';
|
||||
}
|
||||
|
||||
if ($this->connection->doesObjectExist($this->bucket, $path)) {
|
||||
if ($this->connection->doesObjectExist($this->bucket, $this->cleanKey($path))) {
|
||||
return 'dir';
|
||||
}
|
||||
} catch (S3Exception $e) {
|
||||
@@ -306,7 +310,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
try {
|
||||
$result = $this->connection->deleteObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path
|
||||
'Key' => $this->cleanKey($path)
|
||||
));
|
||||
$this->testTimeout();
|
||||
} catch (S3Exception $e) {
|
||||
@@ -329,7 +333,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
try {
|
||||
$result = $this->connection->getObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path,
|
||||
'Key' => $this->cleanKey($path),
|
||||
'SaveAs' => $tmpFile
|
||||
));
|
||||
} catch (S3Exception $e) {
|
||||
@@ -377,7 +381,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
try {
|
||||
$result = $this->connection->headObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path
|
||||
'Key' => $this->cleanKey($path)
|
||||
));
|
||||
} catch (S3Exception $e) {
|
||||
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
||||
@@ -404,7 +408,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
}
|
||||
$result = $this->connection->copyObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path,
|
||||
'Key' => $this->cleanKey($path),
|
||||
'Metadata' => $metadata,
|
||||
'CopySource' => $this->bucket . '/' . $path
|
||||
));
|
||||
@@ -412,7 +416,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
} else {
|
||||
$result = $this->connection->putObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path,
|
||||
'Key' => $this->cleanKey($path),
|
||||
'Metadata' => $metadata
|
||||
));
|
||||
$this->testTimeout();
|
||||
@@ -433,7 +437,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
try {
|
||||
$result = $this->connection->copyObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => $path2,
|
||||
'Key' => $this->cleanKey($path2),
|
||||
'CopySource' => $this->bucket . '/' . $path1
|
||||
));
|
||||
$this->testTimeout();
|
||||
@@ -507,8 +511,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;
|
||||
@@ -530,7 +536,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
|
||||
try {
|
||||
$result= $this->connection->putObject(array(
|
||||
'Bucket' => $this->bucket,
|
||||
'Key' => self::$tmpFiles[$tmpFile],
|
||||
'Key' => $this->cleanKey(self::$tmpFiles[$tmpFile]),
|
||||
'SourceFile' => $tmpFile,
|
||||
'ContentType' => \OC_Helper::getMimeType($tmpFile),
|
||||
'ContentLength' => filesize($tmpFile)
|
||||
|
||||
@@ -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,13 +156,132 @@ 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();
|
||||
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($user);
|
||||
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()
|
||||
* @return array
|
||||
*/
|
||||
public static function getSystemMountPoints() {
|
||||
$mountPoints = self::readData(false);
|
||||
$mountPoints = self::readData();
|
||||
$backends = self::getBackends();
|
||||
$system = array();
|
||||
if (isset($mountPoints[self::MOUNT_TYPE_GROUP])) {
|
||||
@@ -161,20 +291,26 @@ 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
|
||||
if (isset($system[$mountPoint]) && $system[$mountPoint]['configuration'] == $mount['options']) {
|
||||
$system[$mountPoint]['applicable']['groups']
|
||||
= array_merge($system[$mountPoint]['applicable']['groups'], array($group));
|
||||
|
||||
$config = array(
|
||||
'class' => $mount['class'],
|
||||
'mountpoint' => $mountPoint,
|
||||
'backend' => $backends[$mount['class']]['backend'],
|
||||
'options' => $mount['options'],
|
||||
'applicable' => array('groups' => array($group), 'users' => array()),
|
||||
'status' => self::getBackendStatus($mount['class'], $mount['options'])
|
||||
);
|
||||
$hash = self::makeConfigHash($config);
|
||||
// If an existing config exists (with same class, mountpoint and options)
|
||||
if (isset($system[$hash])) {
|
||||
// add the groups into that config
|
||||
$system[$hash]['applicable']['groups']
|
||||
= array_merge($system[$hash]['applicable']['groups'], array($group));
|
||||
} else {
|
||||
$system[$mountPoint] = array(
|
||||
'class' => $mount['class'],
|
||||
'backend' => $backends[$mount['class']]['backend'],
|
||||
'configuration' => $mount['options'],
|
||||
'applicable' => array('groups' => array($group), 'users' => array()),
|
||||
'status' => self::getBackendStatus($mount['class'], $mount['options'])
|
||||
);
|
||||
$system[$hash] = $config;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,25 +322,30 @@ 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
|
||||
if (isset($system[$mountPoint]) && $system[$mountPoint]['configuration'] == $mount['options']) {
|
||||
$system[$mountPoint]['applicable']['users']
|
||||
= array_merge($system[$mountPoint]['applicable']['users'], array($user));
|
||||
$config = array(
|
||||
'class' => $mount['class'],
|
||||
'mountpoint' => $mountPoint,
|
||||
'backend' => $backends[$mount['class']]['backend'],
|
||||
'options' => $mount['options'],
|
||||
'applicable' => array('groups' => array(), 'users' => array($user)),
|
||||
'status' => self::getBackendStatus($mount['class'], $mount['options'])
|
||||
);
|
||||
$hash = self::makeConfigHash($config);
|
||||
// If an existing config exists (with same class, mountpoint and options)
|
||||
if (isset($system[$hash])) {
|
||||
// add the users into that config
|
||||
$system[$hash]['applicable']['users']
|
||||
= array_merge($system[$hash]['applicable']['users'], array($user));
|
||||
} else {
|
||||
$system[$mountPoint] = array(
|
||||
'class' => $mount['class'],
|
||||
'backend' => $backends[$mount['class']]['backend'],
|
||||
'configuration' => $mount['options'],
|
||||
'applicable' => array('groups' => array(), 'users' => array($user)),
|
||||
'status' => self::getBackendStatus($mount['class'], $mount['options'])
|
||||
);
|
||||
$system[$hash] = $config;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $system;
|
||||
return array_values($system);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +354,7 @@ class OC_Mount_Config {
|
||||
* @return array
|
||||
*/
|
||||
public static function getPersonalMountPoints() {
|
||||
$mountPoints = self::readData(true);
|
||||
$mountPoints = self::readData(OCP\User::getUser());
|
||||
$backends = self::getBackends();
|
||||
$uid = OCP\User::getUser();
|
||||
$personal = array();
|
||||
@@ -223,11 +364,13 @@ class OC_Mount_Config {
|
||||
if (strpos($mount['class'], 'OC_Filestorage_') !== false) {
|
||||
$mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15);
|
||||
}
|
||||
// Remove '/uid/files/' from mount point
|
||||
$personal[substr($mountPoint, strlen($uid) + 8)] = array(
|
||||
$mount['options'] = self::decryptPasswords($mount['options']);
|
||||
$personal[] = array(
|
||||
'class' => $mount['class'],
|
||||
// Remove '/uid/files/' from mount point
|
||||
'mountpoint' => substr($mountPoint, strlen($uid) + 8),
|
||||
'backend' => $backends[$mount['class']]['backend'],
|
||||
'configuration' => $mount['options'],
|
||||
'options' => $mount['options'],
|
||||
'status' => self::getBackendStatus($mount['class'], $mount['options'])
|
||||
);
|
||||
}
|
||||
@@ -235,15 +378,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,23 +419,35 @@ 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)));
|
||||
$mountPoints = self::readData($isPersonal);
|
||||
|
||||
$mount = array($applicable => array(
|
||||
$mountPoint => array(
|
||||
'class' => $class,
|
||||
'options' => self::encryptPasswords($classOptions))
|
||||
)
|
||||
);
|
||||
$mountPoints = self::readData($isPersonal ? OCP\User::getUser() : NULL);
|
||||
// Merge the new mount point into the current mount points
|
||||
if (isset($mountPoints[$mountType])) {
|
||||
if (isset($mountPoints[$mountType][$applicable])) {
|
||||
@@ -294,7 +459,7 @@ class OC_Mount_Config {
|
||||
} else {
|
||||
$mountPoints[$mountType] = $mount;
|
||||
}
|
||||
self::writeData($isPersonal, $mountPoints);
|
||||
self::writeData($isPersonal ? OCP\User::getUser() : NULL, $mountPoints);
|
||||
return self::getBackendStatus($class, $classOptions);
|
||||
}
|
||||
|
||||
@@ -316,7 +481,7 @@ class OC_Mount_Config {
|
||||
} else {
|
||||
$mountPoint = '/$user/files/'.ltrim($mountPoint, '/');
|
||||
}
|
||||
$mountPoints = self::readData($isPersonal);
|
||||
$mountPoints = self::readData($isPersonal ? OCP\User::getUser() : NULL);
|
||||
// Remove mount point
|
||||
unset($mountPoints[$mountType][$applicable][$mountPoint]);
|
||||
// Unset parent arrays if empty
|
||||
@@ -326,20 +491,20 @@ class OC_Mount_Config {
|
||||
unset($mountPoints[$mountType]);
|
||||
}
|
||||
}
|
||||
self::writeData($isPersonal, $mountPoints);
|
||||
self::writeData($isPersonal ? OCP\User::getUser() : NULL, $mountPoints);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the mount points in the config file into an array
|
||||
* @param bool Personal or system config file
|
||||
* @param string|null $user If not null, personal for $user, otherwise system
|
||||
* @return array
|
||||
*/
|
||||
private static function readData($isPersonal) {
|
||||
private static function readData($user = NULL) {
|
||||
$parser = new \OC\ArrayParser();
|
||||
if ($isPersonal) {
|
||||
$phpFile = OC_User::getHome(OCP\User::getUser()).'/mount.php';
|
||||
$jsonFile = OC_User::getHome(OCP\User::getUser()).'/mount.json';
|
||||
if (isset($user)) {
|
||||
$phpFile = OC_User::getHome($user).'/mount.php';
|
||||
$jsonFile = OC_User::getHome($user).'/mount.json';
|
||||
} else {
|
||||
$datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data");
|
||||
$phpFile = OC::$SERVERROOT.'/config/mount.php';
|
||||
@@ -361,12 +526,12 @@ class OC_Mount_Config {
|
||||
|
||||
/**
|
||||
* Write the mount points to the config file
|
||||
* @param bool Personal or system config file
|
||||
* @param array Mount points
|
||||
* @param string|null $user If not null, personal for $user, otherwise system
|
||||
* @param array $data Mount points
|
||||
*/
|
||||
private static function writeData($isPersonal, $data) {
|
||||
if ($isPersonal) {
|
||||
$file = OC_User::getHome(OCP\User::getUser()).'/mount.json';
|
||||
private static function writeData($user, $data) {
|
||||
if (isset($user)) {
|
||||
$file = OC_User::getHome($user).'/mount.json';
|
||||
} else {
|
||||
$datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data");
|
||||
$file = $datadir . '/mount.json';
|
||||
@@ -381,8 +546,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 +568,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 +638,87 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a hash based on the given configuration.
|
||||
* This is mostly used to find out whether configurations
|
||||
* are the same.
|
||||
*/
|
||||
private static function makeConfigHash($config) {
|
||||
$data = json_encode(
|
||||
array(
|
||||
'c' => $config['class'],
|
||||
'm' => $config['mountpoint'],
|
||||
'o' => $config['options']
|
||||
)
|
||||
);
|
||||
return hash('md5', $data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +43,8 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
$hostKeys = $this->readHostKeys();
|
||||
$this->client = new \Net_SFTP($this->host);
|
||||
|
||||
if (!$this->client->login($this->user, $this->password)) {
|
||||
throw new \Exception('Login failed');
|
||||
}
|
||||
|
||||
// The SSH Host Key MUST be verified before login().
|
||||
$currentHostKey = $this->client->getServerPublicHostKey();
|
||||
|
||||
if (array_key_exists($this->host, $hostKeys)) {
|
||||
if ($hostKeys[$this->host] != $currentHostKey) {
|
||||
throw new \Exception('Host public key does not match known key');
|
||||
@@ -57,6 +53,10 @@ class SFTP extends \OC\Files\Storage\Common {
|
||||
$hostKeys[$this->host] = $currentHostKey;
|
||||
$this->writeHostKeys($hostKeys);
|
||||
}
|
||||
|
||||
if (!$this->client->login($this->user, $this->password)) {
|
||||
throw new \Exception('Login failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function test() {
|
||||
|
||||
@@ -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{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,17 +16,17 @@
|
||||
</thead>
|
||||
<tbody width="100%">
|
||||
<?php $_['mounts'] = array_merge($_['mounts'], array('' => array())); ?>
|
||||
<?php foreach ($_['mounts'] as $mountPoint => $mount): ?>
|
||||
<tr <?php print_unescaped(($mountPoint != '') ? 'class="'.OC_Util::sanitizeHTML($mount['class']).'"' : 'id="addMountPoint"'); ?>>
|
||||
<?php foreach ($_['mounts'] as $mount): ?>
|
||||
<tr <?php print_unescaped(($mount['mountpoint'] !== '') ? 'class="'.OC_Util::sanitizeHTML($mount['class']).'"' : 'id="addMountPoint"'); ?>>
|
||||
<td class="status">
|
||||
<?php if (isset($mount['status'])): ?>
|
||||
<span class="<?php p(($mount['status']) ? 'success' : 'error'); ?>"></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="mountPoint"><input type="text" name="mountPoint"
|
||||
value="<?php p($mountPoint); ?>"
|
||||
value="<?php p($mount['mountpoint']); ?>"
|
||||
placeholder="<?php p($l->t('Folder name')); ?>" /></td>
|
||||
<?php if ($mountPoint == ''): ?>
|
||||
<?php if ($mount['mountpoint'] == ''): ?>
|
||||
<td class="backend">
|
||||
<select id="selectBackend" data-configurations='<?php print_unescaped(json_encode($_['backends'])); ?>'>
|
||||
<option value="" disabled selected
|
||||
@@ -41,8 +41,8 @@
|
||||
data-class="<?php p($mount['class']); ?>"><?php p($mount['backend']); ?></td>
|
||||
<?php endif; ?>
|
||||
<td class ="configuration" width="100%">
|
||||
<?php if (isset($mount['configuration'])): ?>
|
||||
<?php foreach ($mount['configuration'] as $parameter => $value): ?>
|
||||
<?php if (isset($mount['options'])): ?>
|
||||
<?php foreach ($mount['options'] as $parameter => $value): ?>
|
||||
<?php if (isset($_['backends'][$mount['class']]['configuration'][$parameter])): ?>
|
||||
<?php $placeholder = $_['backends'][$mount['class']]['configuration'][$parameter]; ?>
|
||||
<?php if (strpos($placeholder, '*') !== false): ?>
|
||||
@@ -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); ?>"
|
||||
@@ -108,7 +108,7 @@
|
||||
</select>
|
||||
</td>
|
||||
<?php endif; ?>
|
||||
<td <?php if ($mountPoint != ''): ?>class="remove"
|
||||
<td <?php if ($mount['mountpoint'] != ''): ?>class="remove"
|
||||
<?php else: ?>style="visibility:hidden;"
|
||||
<?php endif ?>><img alt="<?php p($l->t('Delete')); ?>"
|
||||
title="<?php p($l->t('Delete')); ?>"
|
||||
|
||||
@@ -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,531 @@ 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));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provider for testing configurations with different
|
||||
* "applicable" values (all, user, groups)
|
||||
*/
|
||||
public function applicableConfigProvider() {
|
||||
return array(
|
||||
// applicable to "all"
|
||||
array(
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
'all',
|
||||
array(
|
||||
'users' => array('all'),
|
||||
'groups' => array()
|
||||
)
|
||||
),
|
||||
// applicable to single user
|
||||
array(
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
self::TEST_USER1,
|
||||
array(
|
||||
'users' => array(self::TEST_USER1),
|
||||
'groups' => array()
|
||||
)
|
||||
),
|
||||
// applicable to single group
|
||||
array(
|
||||
OC_Mount_Config::MOUNT_TYPE_GROUP,
|
||||
self::TEST_GROUP1,
|
||||
array(
|
||||
'users' => array(),
|
||||
'groups' => array(self::TEST_GROUP1)
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reading and writing global config
|
||||
*
|
||||
* @dataProvider applicableConfigProvider
|
||||
*/
|
||||
public function testReadWriteGlobalConfig($mountType, $applicable, $expectApplicableArray) {
|
||||
$mountType = $mountType;
|
||||
$applicable = $applicable;
|
||||
$isPersonal = false;
|
||||
$options = array(
|
||||
'host' => 'smbhost',
|
||||
'user' => 'smbuser',
|
||||
'password' => 'smbpassword',
|
||||
'share' => 'smbshare',
|
||||
'root' => 'smbroot'
|
||||
);
|
||||
|
||||
// write config
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$options,
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
// re-read config
|
||||
$config = OC_Mount_Config::getSystemMountPoints();
|
||||
$this->assertEquals(1, count($config));
|
||||
$this->assertEquals('\OC\Files\Storage\SMB', $config[0]['class']);
|
||||
$this->assertEquals('ext', $config[0]['mountpoint']);
|
||||
$this->assertEquals($expectApplicableArray, $config[0]['applicable']);
|
||||
$savedOptions = $config[0]['options'];
|
||||
$this->assertEquals($options, $savedOptions);
|
||||
// key order needs to be preserved for the UI...
|
||||
$this->assertEquals(array_keys($options), array_keys($savedOptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test reading and writing config
|
||||
*/
|
||||
public function testReadWritePersonalConfig() {
|
||||
$mountType = OC_Mount_Config::MOUNT_TYPE_USER;
|
||||
$applicable = self::TEST_USER1;
|
||||
$isPersonal = true;
|
||||
$options = array(
|
||||
'host' => 'smbhost',
|
||||
'user' => 'smbuser',
|
||||
'password' => 'smbpassword',
|
||||
'share' => 'smbshare',
|
||||
'root' => 'smbroot'
|
||||
);
|
||||
|
||||
// write config
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$options,
|
||||
$mountType,
|
||||
$applicable,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
// re-read config
|
||||
$config = OC_Mount_Config::getPersonalMountPoints();
|
||||
$this->assertEquals(1, count($config));
|
||||
$this->assertEquals('\OC\Files\Storage\SMB', $config[0]['class']);
|
||||
$this->assertEquals('ext', $config[0]['mountpoint']);
|
||||
$savedOptions = $config[0]['options'];
|
||||
$this->assertEquals($options, $savedOptions);
|
||||
// key order needs to be preserved for the UI...
|
||||
$this->assertEquals(array_keys($options), array_keys($savedOptions));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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[0]['options'];
|
||||
$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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the same config for multiple users.
|
||||
* The config will be merged by getSystemMountPoints().
|
||||
*/
|
||||
public function testConfigMerging() {
|
||||
$mountType = OC_Mount_Config::MOUNT_TYPE_USER;
|
||||
$isPersonal = false;
|
||||
$options = array(
|
||||
'host' => 'smbhost',
|
||||
'user' => 'smbuser',
|
||||
'password' => 'smbpassword',
|
||||
'share' => 'smbshare',
|
||||
'root' => 'smbroot'
|
||||
);
|
||||
|
||||
// write config
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$options,
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
self::TEST_USER1,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$options,
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
self::TEST_USER2,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$options,
|
||||
OC_Mount_Config::MOUNT_TYPE_GROUP,
|
||||
self::TEST_GROUP2,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$options,
|
||||
OC_Mount_Config::MOUNT_TYPE_GROUP,
|
||||
self::TEST_GROUP1,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
// re-read config
|
||||
$config = OC_Mount_Config::getSystemMountPoints();
|
||||
$this->assertEquals(1, count($config));
|
||||
$this->assertEquals('\OC\Files\Storage\SMB', $config[0]['class']);
|
||||
$this->assertEquals('ext', $config[0]['mountpoint']);
|
||||
$this->assertEquals($options, $config[0]['options']);
|
||||
$this->assertEquals(array(self::TEST_USER1, self::TEST_USER2), $config[0]['applicable']['users']);
|
||||
$this->assertEquals(array(self::TEST_GROUP2, self::TEST_GROUP1), $config[0]['applicable']['groups']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create then re-read mount points configs where the mount points
|
||||
* have the same path, the config must NOT be merged.
|
||||
*/
|
||||
public function testRereadMountpointWithSamePath() {
|
||||
$mountType = OC_Mount_Config::MOUNT_TYPE_USER;
|
||||
$isPersonal = false;
|
||||
$options1 = array(
|
||||
'host' => 'smbhost',
|
||||
'user' => 'smbuser',
|
||||
'password' => 'smbpassword',
|
||||
'share' => 'smbshare',
|
||||
'root' => 'smbroot'
|
||||
);
|
||||
|
||||
// write config
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$options1,
|
||||
$mountType,
|
||||
self::TEST_USER1,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
$options2 = array(
|
||||
'host' => 'anothersmbhost',
|
||||
'user' => 'anothersmbuser',
|
||||
'password' => 'anothersmbpassword',
|
||||
'share' => 'anothersmbshare',
|
||||
'root' => 'anothersmbroot'
|
||||
);
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$options2,
|
||||
$mountType,
|
||||
self::TEST_USER2,
|
||||
$isPersonal
|
||||
)
|
||||
);
|
||||
|
||||
// re-read config
|
||||
$config = OC_Mount_Config::getSystemMountPoints();
|
||||
$this->assertEquals(2, count($config));
|
||||
$this->assertEquals('\OC\Files\Storage\SMB', $config[0]['class']);
|
||||
$this->assertEquals('ext', $config[0]['mountpoint']);
|
||||
$this->assertEquals($options1, $config[0]['options']);
|
||||
$this->assertEquals('\OC\Files\Storage\SMB', $config[1]['class']);
|
||||
$this->assertEquals('ext', $config[1]['mountpoint']);
|
||||
$this->assertEquals($options2, $config[1]['options']);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test for correct personal configuration loading in file sharing scenarios
|
||||
*/
|
||||
public function testMultiUserPersonalConfigLoading() {
|
||||
$mountConfig = array(
|
||||
'host' => 'somehost',
|
||||
'user' => 'someuser',
|
||||
'password' => 'somepassword',
|
||||
'root' => 'someroot'
|
||||
);
|
||||
|
||||
// Create personal mount point
|
||||
$this->assertTrue(
|
||||
OC_Mount_Config::addMountPoint(
|
||||
'/ext',
|
||||
'\OC\Files\Storage\SMB',
|
||||
$mountConfig,
|
||||
OC_Mount_Config::MOUNT_TYPE_USER,
|
||||
self::TEST_USER1,
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
// Ensure other user can read mount points
|
||||
\OC_User::setUserId(self::TEST_USER2);
|
||||
$mountPointsMe = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER2);
|
||||
$mountPointsOther = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1);
|
||||
|
||||
$this->assertEquals(0, count($mountPointsMe));
|
||||
$this->assertEquals(1, count($mountPointsOther));
|
||||
$this->assertTrue(isset($mountPointsOther['/'.self::TEST_USER1.'/files/ext']));
|
||||
$this->assertEquals('\OC\Files\Storage\SMB',
|
||||
$mountPointsOther['/'.self::TEST_USER1.'/files/ext']['class']);
|
||||
$this->assertEquals($mountConfig,
|
||||
$mountPointsOther['/'.self::TEST_USER1.'/files/ext']['options']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,34 @@ 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;
|
||||
}
|
||||
|
||||
.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');
|
||||
@@ -243,9 +244,11 @@ if (isset($path)) {
|
||||
$allowZip = OCP\Config::getSystemValue('allowZipDownload', true)
|
||||
&& ( $maxInputFileSize === 0 || $totalSize <= $maxInputFileSize);
|
||||
$tmpl->assign('allowZipDownload', intval($allowZip));
|
||||
$tmpl->assign('showDownloadButton', intval($allowZip));
|
||||
$tmpl->assign('downloadURL',
|
||||
OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=' . urlencode($getPath));
|
||||
} else {
|
||||
$tmpl->assign('showDownloadButton', true);
|
||||
$tmpl->assign('dir', $dir);
|
||||
|
||||
// Show file preview if viewer is available
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<?php /** @var $l OC_L10N */ ?>
|
||||
<div id="notification-container">
|
||||
<div id="notification" style="display: none;"></div>
|
||||
</div>
|
||||
@@ -9,64 +10,19 @@
|
||||
<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>
|
||||
|
||||
<?php if ($_['showDownloadButton']): ?>
|
||||
<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'))?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
</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'])): ?>
|
||||
@@ -74,33 +30,39 @@
|
||||
<?php else: ?>
|
||||
<?php if (substr($_['mimetype'], 0, strpos($_['mimetype'], '/')) == 'image'): ?>
|
||||
<div id="imgframe">
|
||||
<img src="<?php p($_['downloadURL']); ?>" />
|
||||
<img src="<?php p($_['downloadURL']); ?>" alt="" />
|
||||
</div>
|
||||
<?php elseif (substr($_['mimetype'], 0, strpos($_['mimetype'], '/')) == 'video'): ?>
|
||||
<div id="imgframe">
|
||||
<video tabindex="0" controls="" autoplay="">
|
||||
<video tabindex="0" controls="" preload="none">
|
||||
<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"
|
||||
alt="" />
|
||||
</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);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
//OC::$CLASSPATH['OCA\Files_Trashbin\Hooks'] = 'files_trashbin/lib/hooks.php';
|
||||
//OC::$CLASSPATH['OCA\Files_Trashbin\Trashbin'] = 'files_trashbin/lib/trash.php';
|
||||
OC::$CLASSPATH['OCA\Files_Trashbin\Exceptions\CopyRecursiveException'] = 'files_trashbin/lib/exceptions.php';
|
||||
|
||||
|
||||
// register hooks
|
||||
\OCA\Files_Trashbin\Trashbin::registerHooks();
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
Files.updateStorageStatistics = function() {
|
||||
// no op because the trashbin doesn't have
|
||||
// storage info like free space / used space
|
||||
};
|
||||
|
||||
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 +35,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 +71,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 +78,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 +106,8 @@ $(document).ready(function() {
|
||||
var params = {};
|
||||
if (allFiles) {
|
||||
params = {
|
||||
allfiles: true
|
||||
allfiles: true,
|
||||
dir: $('#dir').val()
|
||||
};
|
||||
}
|
||||
else {
|
||||
@@ -153,7 +124,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 +154,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 +214,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(
|
||||
"_%n folder_::_%n folders_" => array("",""),
|
||||
"_%n file_::_%n files_" => array("","")
|
||||
);
|
||||
$PLURAL_FORMS = "nplurals=2; plural=(n != 1);";
|
||||
@@ -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);";
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - trash bin
|
||||
*
|
||||
* @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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Trashbin\Exceptions;
|
||||
|
||||
class CopyRecursiveException extends \Exception {
|
||||
}
|
||||
@@ -44,11 +44,14 @@ 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'];
|
||||
$i['etag'] = $i['timestamp']; // dummy etag
|
||||
$i['mimetype'] = $r['mime'];
|
||||
$i['type'] = $r['type'];
|
||||
if ($i['type'] === 'file') {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user