* This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ namespace Test\User; use OC\AllConfig; use OC\Files\Mount\ObjectHomeMountProvider; use OC\Hooks\PublicEmitter; use OC\User\User; use OCP\Comments\ICommentsManager; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Storage\IStorageFactory; use OCP\IConfig; use OCP\IURLGenerator; use OCP\IUser; use OCP\Notification\IManager as INotificationManager; use OCP\Notification\INotification; use OCP\Server; use OCP\UserInterface; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; /** * Class UserTest * * @group DB * * @package Test\User */ class UserTest extends TestCase { /** @var IEventDispatcher|MockObject */ protected $dispatcher; protected function setUp(): void { parent::setUp(); $this->dispatcher = Server::get(IEventDispatcher::class); } public function testDisplayName() { /** * @var \OC\User\Backend | MockObject $backend */ $backend = $this->createMock(\OC\User\Backend::class); $backend->expects($this->once()) ->method('getDisplayName') ->with($this->equalTo('foo')) ->willReturn('Foo'); $backend->expects($this->any()) ->method('implementsActions') ->with($this->equalTo(\OC\User\Backend::GET_DISPLAYNAME)) ->willReturn(true); $user = new User('foo', $backend, $this->dispatcher); $this->assertEquals('Foo', $user->getDisplayName()); } /** * if the display name contain whitespaces only, we expect the uid as result */ public function testDisplayNameEmpty() { /** * @var \OC\User\Backend | MockObject $backend */ $backend = $this->createMock(\OC\User\Backend::class); $backend->expects($this->once()) ->method('getDisplayName') ->with($this->equalTo('foo')) ->willReturn(' '); $backend->expects($this->any()) ->method('implementsActions') ->with($this->equalTo(\OC\User\Backend::GET_DISPLAYNAME)) ->willReturn(true); $user = new User('foo', $backend, $this->dispatcher); $this->assertEquals('foo', $user->getDisplayName()); } public function testDisplayNameNotSupported() { /** * @var \OC\User\Backend | MockObject $backend */ $backend = $this->createMock(\OC\User\Backend::class); $backend->expects($this->never()) ->method('getDisplayName'); $backend->expects($this->any()) ->method('implementsActions') ->with($this->equalTo(\OC\User\Backend::GET_DISPLAYNAME)) ->willReturn(false); $user = new User('foo', $backend, $this->dispatcher); $this->assertEquals('foo', $user->getDisplayName()); } public function testSetPassword() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) ->method('setPassword') ->with($this->equalTo('foo'), $this->equalTo('bar')); $backend->expects($this->any()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::SET_PASSWORD) { return true; } else { return false; } }); $user = new User('foo', $backend, $this->dispatcher); $this->assertTrue($user->setPassword('bar', '')); } public function testSetPasswordNotSupported() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->never()) ->method('setPassword'); $backend->expects($this->any()) ->method('implementsActions') ->willReturn(false); $user = new User('foo', $backend, $this->dispatcher); $this->assertFalse($user->setPassword('bar', '')); } public function testChangeAvatarSupportedYes() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(AvatarUserDummy::class); $backend->expects($this->once()) ->method('canChangeAvatar') ->with($this->equalTo('foo')) ->willReturn(true); $backend->expects($this->any()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::PROVIDE_AVATAR) { return true; } else { return false; } }); $user = new User('foo', $backend, $this->dispatcher); $this->assertTrue($user->canChangeAvatar()); } public function testChangeAvatarSupportedNo() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(AvatarUserDummy::class); $backend->expects($this->once()) ->method('canChangeAvatar') ->with($this->equalTo('foo')) ->willReturn(false); $backend->expects($this->any()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::PROVIDE_AVATAR) { return true; } else { return false; } }); $user = new User('foo', $backend, $this->dispatcher); $this->assertFalse($user->canChangeAvatar()); } public function testChangeAvatarNotSupported() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(AvatarUserDummy::class); $backend->expects($this->never()) ->method('canChangeAvatar'); $backend->expects($this->any()) ->method('implementsActions') ->willReturn(false); $user = new User('foo', $backend, $this->dispatcher); $this->assertTrue($user->canChangeAvatar()); } public function testDelete() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) ->method('deleteUser') ->with($this->equalTo('foo')); $user = new User('foo', $backend, $this->dispatcher); $this->assertTrue($user->delete()); } public function testDeleteWithDifferentHome() { /** @var ObjectHomeMountProvider $homeProvider */ $homeProvider = \OC::$server->get(ObjectHomeMountProvider::class); $user = $this->createMock(IUser::class); $user->method('getUID') ->willReturn('foo'); if ($homeProvider->getHomeMountForUser($user, $this->createMock(IStorageFactory::class)) !== null) { $this->markTestSkipped("Skipping test for non local home storage"); } /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::GET_HOME) { return true; } else { return false; } }); // important: getHome MUST be called before deleteUser because // once the user is deleted, getHome implementations might not // return anything $backend->expects($this->once()) ->method('getHome') ->with($this->equalTo('foo')) ->willReturn('/home/foo'); $backend->expects($this->once()) ->method('deleteUser') ->with($this->equalTo('foo')); $user = new User('foo', $backend, $this->dispatcher); $this->assertTrue($user->delete()); } public function testGetHome() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) ->method('getHome') ->with($this->equalTo('foo')) ->willReturn('/home/foo'); $backend->expects($this->any()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::GET_HOME) { return true; } else { return false; } }); $user = new User('foo', $backend, $this->dispatcher); $this->assertEquals('/home/foo', $user->getHome()); } public function testGetBackendClassName() { $user = new User('foo', new \Test\Util\User\Dummy(), $this->dispatcher); $this->assertEquals('Dummy', $user->getBackendClassName()); $user = new User('foo', new \OC\User\Database(), $this->dispatcher); $this->assertEquals('Database', $user->getBackendClassName()); } public function testGetHomeNotSupported() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->never()) ->method('getHome'); $backend->expects($this->any()) ->method('implementsActions') ->willReturn(false); $allConfig = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor() ->getMock(); $allConfig->expects($this->any()) ->method('getUserValue') ->willReturn(true); $allConfig->expects($this->any()) ->method('getSystemValueString') ->with($this->equalTo('datadirectory')) ->willReturn('arbitrary/path'); $user = new User('foo', $backend, $this->dispatcher, null, $allConfig); $this->assertEquals('arbitrary/path/foo', $user->getHome()); } public function testCanChangePassword() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->any()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::SET_PASSWORD) { return true; } else { return false; } }); $user = new User('foo', $backend, $this->dispatcher); $this->assertTrue($user->canChangePassword()); } public function testCanChangePasswordNotSupported() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->any()) ->method('implementsActions') ->willReturn(false); $user = new User('foo', $backend, $this->dispatcher); $this->assertFalse($user->canChangePassword()); } public function testCanChangeDisplayName() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->any()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::SET_DISPLAYNAME) { return true; } else { return false; } }); $config = $this->createMock(IConfig::class); $config->method('getSystemValueBool') ->with('allow_user_to_change_display_name') ->willReturn(true); $user = new User('foo', $backend, $this->dispatcher, null, $config); $this->assertTrue($user->canChangeDisplayName()); } public function testCanChangeDisplayNameNotSupported() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->any()) ->method('implementsActions') ->willReturn(false); $user = new User('foo', $backend, $this->dispatcher); $this->assertFalse($user->canChangeDisplayName()); } public function testSetDisplayNameSupported() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\OC\User\Database::class); $backend->expects($this->any()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::SET_DISPLAYNAME) { return true; } else { return false; } }); $backend->expects($this->once()) ->method('setDisplayName') ->with('foo', 'Foo') ->willReturn(true); $user = new User('foo', $backend, $this->createMock(IEventDispatcher::class)); $this->assertTrue($user->setDisplayName('Foo')); $this->assertEquals('Foo', $user->getDisplayName()); } /** * don't allow display names containing whitespaces only */ public function testSetDisplayNameEmpty() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\OC\User\Database::class); $backend->expects($this->any()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::SET_DISPLAYNAME) { return true; } else { return false; } }); $user = new User('foo', $backend, $this->dispatcher); $this->assertFalse($user->setDisplayName(' ')); $this->assertEquals('foo', $user->getDisplayName()); } public function testSetDisplayNameNotSupported() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\OC\User\Database::class); $backend->expects($this->any()) ->method('implementsActions') ->willReturn(false); $backend->expects($this->never()) ->method('setDisplayName'); $user = new User('foo', $backend, $this->dispatcher); $this->assertFalse($user->setDisplayName('Foo')); $this->assertEquals('foo', $user->getDisplayName()); } public function testSetPasswordHooks() { $hooksCalled = 0; $test = $this; /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) ->method('setPassword'); /** * @param User $user * @param string $password */ $hook = function ($user, $password) use ($test, &$hooksCalled) { $hooksCalled++; $test->assertEquals('foo', $user->getUID()); $test->assertEquals('bar', $password); }; $emitter = new PublicEmitter(); $emitter->listen('\OC\User', 'preSetPassword', $hook); $emitter->listen('\OC\User', 'postSetPassword', $hook); $backend->expects($this->any()) ->method('implementsActions') ->willReturnCallback(function ($actions) { if ($actions === \OC\User\Backend::SET_PASSWORD) { return true; } else { return false; } }); $user = new User('foo', $backend, $this->dispatcher, $emitter); $user->setPassword('bar', ''); $this->assertEquals(2, $hooksCalled); } public function dataDeleteHooks() { return [ [true, 2], [false, 1], ]; } /** * @dataProvider dataDeleteHooks * @param bool $result * @param int $expectedHooks */ public function testDeleteHooks($result, $expectedHooks) { $hooksCalled = 0; $test = $this; /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $backend->expects($this->once()) ->method('deleteUser') ->willReturn($result); $emitter = new PublicEmitter(); $user = new User('foo', $backend, $this->dispatcher, $emitter); /** * @param User $user */ $hook = function ($user) use ($test, &$hooksCalled) { $hooksCalled++; $test->assertEquals('foo', $user->getUID()); }; $emitter->listen('\OC\User', 'preDelete', $hook); $emitter->listen('\OC\User', 'postDelete', $hook); $config = $this->createMock(IConfig::class); $commentsManager = $this->createMock(ICommentsManager::class); $notificationManager = $this->createMock(INotificationManager::class); $config->method('getSystemValue') ->willReturnArgument(1); $config->method('getSystemValueString') ->willReturnArgument(1); $config->method('getSystemValueBool') ->willReturnArgument(1); $config->method('getSystemValueInt') ->willReturnArgument(1); if ($result) { $config->expects($this->once()) ->method('deleteAllUserValues') ->with('foo'); $commentsManager->expects($this->once()) ->method('deleteReferencesOfActor') ->with('users', 'foo'); $commentsManager->expects($this->once()) ->method('deleteReadMarksFromUser') ->with($user); $notification = $this->createMock(INotification::class); $notification->expects($this->once()) ->method('setUser') ->with('foo'); $notificationManager->expects($this->once()) ->method('createNotification') ->willReturn($notification); $notificationManager->expects($this->once()) ->method('markProcessed') ->with($notification); } else { $config->expects($this->never()) ->method('deleteAllUserValues'); $commentsManager->expects($this->never()) ->method('deleteReferencesOfActor'); $commentsManager->expects($this->never()) ->method('deleteReadMarksFromUser'); $notificationManager->expects($this->never()) ->method('createNotification'); $notificationManager->expects($this->never()) ->method('markProcessed'); } $this->overwriteService(\OCP\Notification\IManager::class, $notificationManager); $this->overwriteService(\OCP\Comments\ICommentsManager::class, $commentsManager); $this->overwriteService(AllConfig::class, $config); $this->assertSame($result, $user->delete()); $this->restoreService(AllConfig::class); $this->restoreService(\OCP\Comments\ICommentsManager::class); $this->restoreService(\OCP\Notification\IManager::class); $this->assertEquals($expectedHooks, $hooksCalled); } public function dataGetCloudId(): array { return [ ['https://localhost:8888/nextcloud', 'foo@localhost:8888/nextcloud'], ['http://localhost:8888/nextcloud', 'foo@http://localhost:8888/nextcloud'], ]; } /** * @dataProvider dataGetCloudId */ public function testGetCloudId(string $absoluteUrl, string $cloudId): void { /** @var Backend|MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $urlGenerator = $this->createMock(IURLGenerator::class); $urlGenerator->method('getAbsoluteURL') ->withAnyParameters() ->willReturn($absoluteUrl); $user = new User('foo', $backend, $this->dispatcher, null, null, $urlGenerator); $this->assertEquals($cloudId, $user->getCloudId()); } public function testSetEMailAddressEmpty() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $test = $this; $hooksCalled = 0; /** * @param IUser $user * @param string $feature * @param string $value */ $hook = function (IUser $user, $feature, $value) use ($test, &$hooksCalled) { $hooksCalled++; $test->assertEquals('eMailAddress', $feature); $test->assertEquals('', $value); }; $emitter = new PublicEmitter(); $emitter->listen('\OC\User', 'changeUser', $hook); $config = $this->createMock(IConfig::class); $config->expects($this->once()) ->method('deleteUserValue') ->with( 'foo', 'settings', 'email' ); $user = new User('foo', $backend, $this->dispatcher, $emitter, $config); $user->setEMailAddress(''); } public function testSetEMailAddress() { /** * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $test = $this; $hooksCalled = 0; /** * @param IUser $user * @param string $feature * @param string $value */ $hook = function (IUser $user, $feature, $value) use ($test, &$hooksCalled) { $hooksCalled++; $test->assertEquals('eMailAddress', $feature); $test->assertEquals('foo@bar.com', $value); }; $emitter = new PublicEmitter(); $emitter->listen('\OC\User', 'changeUser', $hook); $config = $this->createMock(IConfig::class); $config->expects($this->once()) ->method('setUserValue') ->with( 'foo', 'settings', 'email', 'foo@bar.com' ); $user = new User('foo', $backend, $this->dispatcher, $emitter, $config); $user->setEMailAddress('foo@bar.com'); } public function testSetEMailAddressNoChange() { /** * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); /** @var PublicEmitter|MockObject $emitter */ $emitter = $this->createMock(PublicEmitter::class); $emitter->expects($this->never()) ->method('emit'); $dispatcher = $this->createMock(IEventDispatcher::class); $dispatcher->expects($this->never()) ->method('dispatch'); $config = $this->createMock(IConfig::class); $config->expects($this->any()) ->method('getUserValue') ->willReturn('foo@bar.com'); $config->expects($this->any()) ->method('setUserValue'); $user = new User('foo', $backend, $dispatcher, $emitter, $config); $user->setEMailAddress('foo@bar.com'); } public function testSetQuota() { /** * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $test = $this; $hooksCalled = 0; /** * @param IUser $user * @param string $feature * @param string $value */ $hook = function (IUser $user, $feature, $value) use ($test, &$hooksCalled) { $hooksCalled++; $test->assertEquals('quota', $feature); $test->assertEquals('23 TB', $value); }; $emitter = new PublicEmitter(); $emitter->listen('\OC\User', 'changeUser', $hook); $config = $this->createMock(IConfig::class); $config->expects($this->once()) ->method('setUserValue') ->with( 'foo', 'files', 'quota', '23 TB' ); $user = new User('foo', $backend, $this->dispatcher, $emitter, $config); $user->setQuota('23 TB'); } public function testGetDefaultUnlimitedQuota() { /** * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); /** @var PublicEmitter|MockObject $emitter */ $emitter = $this->createMock(PublicEmitter::class); $emitter->expects($this->never()) ->method('emit'); $config = $this->createMock(IConfig::class); $user = new User('foo', $backend, $this->dispatcher, $emitter, $config); $userValueMap = [ ['foo', 'files', 'quota', 'default', 'default'], ]; $appValueMap = [ ['files', 'default_quota', 'none', 'none'], // allow unlimited quota ['files', 'allow_unlimited_quota', '1', '1'], ]; $config->method('getUserValue') ->will($this->returnValueMap($userValueMap)); $config->method('getAppValue') ->will($this->returnValueMap($appValueMap)); $quota = $user->getQuota(); $this->assertEquals('none', $quota); } public function testGetDefaultUnlimitedQuotaForbidden() { /** * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); /** @var PublicEmitter|MockObject $emitter */ $emitter = $this->createMock(PublicEmitter::class); $emitter->expects($this->never()) ->method('emit'); $config = $this->createMock(IConfig::class); $user = new User('foo', $backend, $this->dispatcher, $emitter, $config); $userValueMap = [ ['foo', 'files', 'quota', 'default', 'default'], ]; $appValueMap = [ ['files', 'default_quota', 'none', 'none'], // do not allow unlimited quota ['files', 'allow_unlimited_quota', '1', '0'], ['files', 'quota_preset', '1 GB, 5 GB, 10 GB', '1 GB, 5 GB, 10 GB'], // expect seeing 1 GB used as fallback value ['files', 'default_quota', '1 GB', '1 GB'], ]; $config->method('getUserValue') ->will($this->returnValueMap($userValueMap)); $config->method('getAppValue') ->will($this->returnValueMap($appValueMap)); $quota = $user->getQuota(); $this->assertEquals('1 GB', $quota); } public function testSetQuotaAddressNoChange() { /** * @var UserInterface | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); /** @var PublicEmitter|MockObject $emitter */ $emitter = $this->createMock(PublicEmitter::class); $emitter->expects($this->never()) ->method('emit'); $config = $this->createMock(IConfig::class); $config->expects($this->any()) ->method('getUserValue') ->willReturn('23 TB'); $config->expects($this->never()) ->method('setUserValue'); $user = new User('foo', $backend, $this->dispatcher, $emitter, $config); $user->setQuota('23 TB'); } public function testGetLastLogin() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $config = $this->createMock(IConfig::class); $config->method('getUserValue') ->willReturnCallback(function ($uid, $app, $key, $default) { if ($uid === 'foo' && $app === 'login' && $key === 'lastLogin') { return 42; } else { return $default; } }); $user = new User('foo', $backend, $this->dispatcher, null, $config); $this->assertSame(42, $user->getLastLogin()); } public function testSetEnabled() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $config = $this->createMock(IConfig::class); $config->expects($this->once()) ->method('setUserValue') ->with( $this->equalTo('foo'), $this->equalTo('core'), $this->equalTo('enabled'), 'true' ); $user = new User('foo', $backend, $this->dispatcher, null, $config); $user->setEnabled(true); } public function testSetDisabled() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $config = $this->createMock(IConfig::class); $config->expects($this->once()) ->method('setUserValue') ->with( $this->equalTo('foo'), $this->equalTo('core'), $this->equalTo('enabled'), 'false' ); $user = $this->getMockBuilder(User::class) ->setConstructorArgs([ 'foo', $backend, $this->dispatcher, null, $config, ]) ->setMethods(['isEnabled', 'triggerChange']) ->getMock(); $user->expects($this->once()) ->method('isEnabled') ->willReturn(true); $user->expects($this->once()) ->method('triggerChange') ->with( 'enabled', false ); $user->setEnabled(false); } public function testSetDisabledAlreadyDisabled() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $config = $this->createMock(IConfig::class); $config->expects($this->never()) ->method('setUserValue'); $user = $this->getMockBuilder(User::class) ->setConstructorArgs([ 'foo', $backend, $this->dispatcher, null, $config, ]) ->setMethods(['isEnabled', 'triggerChange']) ->getMock(); $user->expects($this->once()) ->method('isEnabled') ->willReturn(false); $user->expects($this->never()) ->method('triggerChange'); $user->setEnabled(false); } public function testGetEMailAddress() { /** * @var Backend | MockObject $backend */ $backend = $this->createMock(\Test\Util\User\Dummy::class); $config = $this->createMock(IConfig::class); $config->method('getUserValue') ->willReturnCallback(function ($uid, $app, $key, $default) { if ($uid === 'foo' && $app === 'settings' && $key === 'email') { return 'foo@bar.com'; } else { return $default; } }); $user = new User('foo', $backend, $this->dispatcher, null, $config); $this->assertSame('foo@bar.com', $user->getEMailAddress()); } } e='backport/47883/stable30'>backport/47883/stable30 Nextcloud server, a safe home for all your data: https://github.com/nextcloud/serverwww-data
aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Files/Node/File.php
blob: eb6411d7d137c24082b1d6015a78d79784000b25 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
<?php

/**
 * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
 * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
 * SPDX-License-Identifier: AGPL-3.0-only
 */
namespace OC\Files\Node;

use OCP\Files\GenericFileException;
use OCP\Files\NotPermittedException;
use OCP\Lock\LockedException;

class File extends Node implements \OCP\Files\File {
	/**
	 * Creates a Folder that represents a non-existing path
	 *
	 * @param string $path path
	 * @return NonExistingFile non-existing node
	 */
	protected function createNonExistingNode($path) {
		return new NonExistingFile($this->root, $this->view, $path);
	}

	/**
	 * @return string
	 * @throws NotPermittedException
	 * @throws GenericFileException
	 * @throws LockedException
	 */
	public function getContent() {
		if ($this->checkPermissions(\OCP\Constants::PERMISSION_READ)) {
			$content = $this->view->file_get_contents($this->path);
			if ($content === false) {
				throw new GenericFileException();
			}
			return $content;
		} else {
			throw new NotPermittedException();
		}
	}

	/**
	 * @param string|resource $data
	 * @throws NotPermittedException
	 * @throws GenericFileException
	 * @throws LockedException
	 */
	public function putContent($data) {
		if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
			$this->sendHooks(['preWrite']);
			if ($this->view->file_put_contents($this->path, $data) === false) {
				throw new GenericFileException('file_put_contents failed');
			}
			$this->fileInfo = null;
			$this->sendHooks(['postWrite']);
		} else {
			throw new NotPermittedException();
		}
	}

	/**
	 * @param string $mode
	 * @return resource|false
	 * @throws NotPermittedException
	 * @throws LockedException
	 */
	public function fopen($mode) {
		$preHooks = [];
		$postHooks = [];
		$requiredPermissions = \OCP\Constants::PERMISSION_READ;
		switch ($mode) {
			case 'r+':
			case 'rb+':
			case 'w+':
			case 'wb+':
			case 'x+':
			case 'xb+':
			case 'a+':
			case 'ab+':
			case 'w':
			case 'wb':
			case 'x':
			case 'xb':
			case 'a':
			case 'ab':
				$preHooks[] = 'preWrite';
				$postHooks[] = 'postWrite';
				$requiredPermissions |= \OCP\Constants::PERMISSION_UPDATE;
				break;
		}

		if ($this->checkPermissions($requiredPermissions)) {
			$this->sendHooks($preHooks);
			$result = $this->view->fopen($this->path, $mode);
			$this->sendHooks($postHooks);
			return $result;
		} else {
			throw new NotPermittedException();
		}
	}

	/**
	 * @throws NotPermittedException
	 * @throws \OCP\Files\InvalidPathException
	 * @throws \OCP\Files\NotFoundException
	 */
	public function delete() {
		if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
			$this->sendHooks(['preDelete']);
			$fileInfo = $this->getFileInfo();
			$this->view->unlink($this->path);
			$nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo);
			$this->sendHooks(['postDelete'], [$nonExisting]);
			$this->fileInfo = null;
		} else {
			throw new NotPermittedException();
		}
	}

	/**
	 * @param string $type
	 * @param bool $raw
	 * @return string
	 */
	public function hash($type, $raw = false) {
		return $this->view->hash($type, $this->path, $raw);
	}

	/**
	 * @inheritdoc
	 */
	public function getChecksum() {
		return $this->getFileInfo()->getChecksum();
	}

	public function getExtension(): string {
		return $this->getFileInfo()->getExtension();
	}
}