Completed
Push — master ( ec6e3a...a32d5d )
by Thomas
10:38
created

ChunkingPluginZsync::beforeMove()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 10
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 2
dl 10
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/*
3
 * Copyright (C) by Ahmed Ammar <[email protected]>
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
8
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
 *
10
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
 * Software.
12
 *
13
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
 *
18
 */
19
namespace OCA\DAV\Upload;
20
21
22
use OCA\DAV\Connector\Sabre\File;
23
use Sabre\DAV\Exception\BadRequest;
24
use Sabre\DAV\Server;
25
use Sabre\DAV\ServerPlugin;
26
use Sabre\DAV\Exception\NotFound;
27
use OC\Files\View;
28
29
class ChunkingPluginZsync extends ServerPlugin {
30
31
	/** @var Server */
32
	private $server;
33
	/** @var FutureFileZsync */
34
	private $sourceNode;
35
	/** @var OC\Files\View */
36
	private $view;
37
38
	public function __construct(View $view) {
39
		$this->view = $view;
0 ignored issues
show
Documentation Bug introduced by
It seems like $view of type object<OC\Files\View> is incompatible with the declared type object<OCA\DAV\Upload\OC\Files\View> of property $view.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
40
		$this->view->mkdir('files_zsync');
41
	}
42
43
	/**
44
	 * @inheritdoc
45
	 */
46
	function initialize(Server $server) {
47
		$server->on('beforeMove', [$this, 'beforeMove']);
48
		$this->server = $server;
49
	}
50
51
	/**
52
	 * @param string $sourcePath source path
53
	 * @param string $destination destination path
54
	 */
55 View Code Duplication
	function beforeMove($sourcePath, $destination) {
56
		$this->sourceNode = $this->server->tree->getNodeForPath($sourcePath);
57
		if (!$this->sourceNode instanceof FutureFileZsync) {
58
			// skip handling as the source is not a chunked FutureFileZsync
59
			return;
60
		}
61
62
		$this->verifySize();
63
		return $this->performMove($sourcePath, $destination);
64
	}
65
66
	/**
67
	 * Handles the temporary copy of the zsync metadata file
68
	 *
69
	 * Will not execute on external storage.
70
	 *
71
	 * @param string $path metadata path
72
	 * @param string $destination destination path
73
	 */
74
	private function preMoveZsync($path, $destination) {
75
		try {
76
			$node = $this->server->tree->getNodeForPath($destination);
77
		} catch (NotFound $e) {
78
			$node = $this->server->tree->getNodeForPath(dirname($destination));
79
		}
80
81
		// Disable if external storage used.
82
		if (strpos($node->getDavPermissions(), 'M') === false) {
83
			$zsyncMetadataNode = $this->server->tree->getNodeForPath($path);
84
			$zsyncMetadataHandle = $zsyncMetadataNode->get();
85
86
			// get .zsync contents before its deletion
87
			$zsyncMetadata = '';
88
			while (!feof($zsyncMetadataHandle)) {
89
				$zsyncMetadata .= fread($zsyncMetadataHandle, $zsyncMetadataNode->getSize());
90
			}
91
			fclose($zsyncMetadataHandle);
92
93
			if ($this->server->tree->nodeExists($destination)) {
94
				// set backingFile which is needed by AssemblyStreamZsync
95
				$backingFile = $this->server->tree->getNodeForPath($destination);
96
				$this->sourceNode->setBackingFile($backingFile);
0 ignored issues
show
Compatibility introduced by
$backingFile of type object<Sabre\DAV\INode> is not a sub-type of object<Sabre\DAV\IFile>. It seems like you assume a child interface of the interface Sabre\DAV\INode to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
97
			}
98
99
			$fileLength = $this->server->httpRequest->getHeader('OC-Total-File-Length');
100
			$this->sourceNode->setFileLength($fileLength);
101
102
			return $zsyncMetadata;
103
		}
104
	}
105
106
	/**
107
	 * Handles the creation of the zsync metadata file
108
	 *
109
	 * @param string &$zsyncMetadata actual metadata
110
	 * @param string $destination destination path
111
	 */
112
	private function postMoveZsync(&$zsyncMetadata, $destination) {
113
		if (!$zsyncMetadata)
114
			return;
115
		$destination = implode('/', array_slice(explode('/', $destination), 2));
116
		$info = $this->view->getFileInfo('files/'.$destination);
117
		$zsyncMetadataFile = 'files_zsync/'.$info->getId();
118
		$this->view->file_put_contents($zsyncMetadataFile, $zsyncMetadata);
119
	}
120
121
	/**
122
	 * Move handler for future file.
123
	 *
124
	 * This overrides the default move behavior to prevent Sabre
125
	 * to delete the target file before moving. Because deleting would
126
	 * lose the file id and metadata.
127
	 *
128
	 * @param string $path source path
129
	 * @param string $destination destination path
130
	 * @return bool|void false to stop handling, void to skip this handler
131
	 */
132
	public function performMove($path, $destination) {
133
		$response = $this->server->httpResponse;
134
		$response->setHeader('Content-Length', '0');
135
		$this->server->tree->nodeExists($destination) ? $response->setStatus(204) : $response->setStatus(201);
136
137
		// copy the zsync metadata file contents, before it gets removed.
138
		$zsyncMetadataPath = dirname($path).'/.zsync';
139
		$zsyncMetadata = $this->preMoveZsync($zsyncMetadataPath, $destination);
140
141
		// do a move manually, skipping Sabre's default "delete" for existing nodes
142
		$this->server->tree->move($path, $destination);
143
144
		// create the zsync metadata file
145
		$this->postMoveZsync($zsyncMetadata, $destination);
146
147
		// trigger all default events (copied from CorePlugin::move)
148
		$this->server->emit('afterMove', [$path, $destination]);
149
		$this->server->emit('afterUnbind', [$path]);
150
		$this->server->emit('afterBind', [$destination]);
151
152
		return false;
153
	}
154
155
	/**
156
	 * @throws BadRequest
157
	 */
158 View Code Duplication
	private function verifySize() {
159
		$expectedSize = $this->server->httpRequest->getHeader('OC-Total-Length');
160
		if ($expectedSize === null) {
161
			return;
162
		}
163
		$actualSize = $this->sourceNode->getSize();
164
		if ((int)$expectedSize !== $actualSize) {
165
			throw new BadRequest("Chunks on server do not sum up to $expectedSize but to $actualSize");
166
		}
167
	}
168
}
169