Passed
Push — master ( 63319e...b594f7 )
by Robin
03:29
created

NativeShare::del()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 2
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Copyright (c) 2014 Robin Appelman <[email protected]>
4
 * This file is licensed under the Licensed under the MIT license:
5
 * http://opensource.org/licenses/MIT
6
 */
7
8
namespace Icewind\SMB\Native;
9
10
use Icewind\SMB\AbstractShare;
11
use Icewind\SMB\Exception\DependencyException;
12
use Icewind\SMB\Exception\InvalidPathException;
13
use Icewind\SMB\Exception\InvalidResourceException;
14
use Icewind\SMB\INotifyHandler;
15
use Icewind\SMB\IServer;
16
use Icewind\SMB\Wrapped\Server;
17
use Icewind\SMB\Wrapped\Share;
18
19
class NativeShare extends AbstractShare {
20
	/**
21
	 * @var IServer $server
22
	 */
23
	private $server;
24
25
	/**
26
	 * @var string $name
27
	 */
28
	private $name;
29
30
	/**
31
	 * @var NativeState $state
32
	 */
33
	private $state;
34
35
	/**
36
	 * @param IServer $server
37
	 * @param string $name
38
	 */
39 254
	public function __construct($server, $name) {
40 254
		parent::__construct();
41 254
		$this->server = $server;
42 254
		$this->name = $name;
43 254
	}
44
45
	/**
46
	 * @throws \Icewind\SMB\Exception\ConnectionException
47
	 * @throws \Icewind\SMB\Exception\AuthenticationException
48
	 * @throws \Icewind\SMB\Exception\InvalidHostException
49
	 */
50 254
	protected function getState() {
51 254
		if ($this->state and $this->state instanceof NativeState) {
52 254
			return $this->state;
53
		}
54
55 254
		$this->state = new NativeState();
56 254
		$this->state->init($this->server->getAuth(), $this->server->getOptions());
57 254
		return $this->state;
58
	}
59
60
	/**
61
	 * Get the name of the share
62
	 *
63
	 * @return string
64
	 */
65
	public function getName() {
66
		return $this->name;
67
	}
68
69 254
	private function buildUrl($path) {
70 254
		$this->verifyPath($path);
71 254
		$url = sprintf('smb://%s/%s', $this->server->getHost(), $this->name);
72 254
		if ($path) {
73 254
			$path = trim($path, '/');
74 254
			$url .= '/';
75 254
			$url .= implode('/', array_map('rawurlencode', explode('/', $path)));
76
		}
77 254
		return $url;
78
	}
79
80
	/**
81
	 * List the content of a remote folder
82
	 *
83
	 * @param string $path
84
	 * @return \Icewind\SMB\IFileInfo[]
85
	 *
86
	 * @throws \Icewind\SMB\Exception\NotFoundException
87
	 * @throws \Icewind\SMB\Exception\InvalidTypeException
88
	 */
89 254
	public function dir($path) {
90 254
		$files = [];
91
92 254
		$dh = $this->getState()->opendir($this->buildUrl($path));
93 254
		while ($file = $this->getState()->readdir($dh)) {
94 254
			$name = $file['name'];
95 254
			if ($name !== '.' and $name !== '..') {
96 102
				$fullPath = $path . '/' . $name;
97 102
				$files [] = new NativeFileInfo($this, $fullPath, $name);
98
			}
99
		}
100
101 254
		$this->getState()->closedir($dh);
102 254
		return $files;
103
	}
104
105
	/**
106
	 * @param string $path
107
	 * @return \Icewind\SMB\IFileInfo
108
	 */
109 32
	public function stat($path) {
110 32
		$info = new NativeFileInfo($this, $path, self::mb_basename($path));
111
112
		// trigger attribute loading
113 32
		$info->getSize();
114
115 22
		return $info;
116
	}
117
118
	/**
119
	 * Multibyte unicode safe version of basename()
120
	 *
121
	 * @param string $path
122
	 * @link http://php.net/manual/en/function.basename.php#121405
123
	 * @return string
124
	 */
125 32
	protected static function mb_basename($path) {
126 32
		if (preg_match('@^.*[\\\\/]([^\\\\/]+)$@s', $path, $matches)) {
127 22
			return $matches[1];
128 10
		} elseif (preg_match('@^([^\\\\/]+)$@s', $path, $matches)) {
129 9
			return $matches[1];
130
		}
131
132 1
		return '';
133
	}
134
135
	private function getStat($path) {
0 ignored issues
show
Unused Code introduced by
The method getStat() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
136
		return $this->getState()->stat($this->buildUrl($path));
137
	}
138
139
	/**
140
	 * Create a folder on the share
141
	 *
142
	 * @param string $path
143
	 * @return bool
144
	 *
145
	 * @throws \Icewind\SMB\Exception\NotFoundException
146
	 * @throws \Icewind\SMB\Exception\AlreadyExistsException
147
	 */
148 254
	public function mkdir($path) {
149 254
		return $this->getState()->mkdir($this->buildUrl($path));
150
	}
151
152
	/**
153
	 * Remove a folder on the share
154
	 *
155
	 * @param string $path
156
	 * @return bool
157
	 *
158
	 * @throws \Icewind\SMB\Exception\NotFoundException
159
	 * @throws \Icewind\SMB\Exception\InvalidTypeException
160
	 */
161 254
	public function rmdir($path) {
162 254
		return $this->getState()->rmdir($this->buildUrl($path));
163
	}
164
165
	/**
166
	 * Delete a file on the share
167
	 *
168
	 * @param string $path
169
	 * @return bool
170
	 *
171
	 * @throws \Icewind\SMB\Exception\NotFoundException
172
	 * @throws \Icewind\SMB\Exception\InvalidTypeException
173
	 */
174 133
	public function del($path) {
175 133
		return $this->getState()->unlink($this->buildUrl($path));
176
	}
177
178
	/**
179
	 * Rename a remote file
180
	 *
181
	 * @param string $from
182
	 * @param string $to
183
	 * @return bool
184
	 *
185
	 * @throws \Icewind\SMB\Exception\NotFoundException
186
	 * @throws \Icewind\SMB\Exception\AlreadyExistsException
187
	 */
188 28
	public function rename($from, $to) {
189 28
		return $this->getState()->rename($this->buildUrl($from), $this->buildUrl($to));
190
	}
191
192
	/**
193
	 * Upload a local file
194
	 *
195
	 * @param string $source local file
196
	 * @param string $target remove file
197
	 * @return bool
198
	 *
199
	 * @throws \Icewind\SMB\Exception\NotFoundException
200
	 * @throws \Icewind\SMB\Exception\InvalidTypeException
201
	 */
202 112
	public function put($source, $target) {
203 112
		$sourceHandle = fopen($source, 'rb');
204 112
		$targetHandle = $this->getState()->create($this->buildUrl($target));
205
206 101
		while ($data = fread($sourceHandle, NativeReadStream::CHUNK_SIZE)) {
0 ignored issues
show
Bug introduced by
It seems like $sourceHandle can also be of type false; however, parameter $handle of fread() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

206
		while ($data = fread(/** @scrutinizer ignore-type */ $sourceHandle, NativeReadStream::CHUNK_SIZE)) {
Loading history...
207 101
			$this->getState()->write($targetHandle, $data);
208
		}
209 101
		$this->getState()->close($targetHandle);
210 101
		return true;
211
	}
212
213
	/**
214
	 * Download a remote file
215
	 *
216
	 * @param string $source remove file
217
	 * @param string $target local file
218
	 * @return bool
219
	 *
220
	 * @throws \Icewind\SMB\Exception\NotFoundException
221
	 * @throws \Icewind\SMB\Exception\InvalidTypeException
222
	 * @throws \Icewind\SMB\Exception\InvalidPathException
223
	 * @throws \Icewind\SMB\Exception\InvalidResourceException
224
	 */
225 45
	public function get($source, $target) {
226 45
		if (!$target) {
227 9
			throw new InvalidPathException('Invalid target path: Filename cannot be empty');
228
		}
229
230 36
		$sourceHandle = $this->getState()->open($this->buildUrl($source), 'r');
231 34
		if (!$sourceHandle) {
0 ignored issues
show
introduced by
$sourceHandle is of type resource, thus it always evaluated to false.
Loading history...
232
			throw new InvalidResourceException('Failed opening remote file "' . $source . '" for reading');
233
		}
234
235 34
		$targetHandle = @fopen($target, 'wb');
236 34
		if (!$targetHandle) {
237 1
			$error = error_get_last();
238 1
			if (is_array($error)) {
239 1
				$reason = $error['message'];
240
			} else {
241
				$reason = 'Unknown error';
242
			}
243 1
			$this->getState()->close($sourceHandle);
244 1
			throw new InvalidResourceException('Failed opening local file "' . $target . '" for writing: ' . $reason);
245
		}
246
247 33
		while ($data = $this->getState()->read($sourceHandle, NativeReadStream::CHUNK_SIZE)) {
248 33
			fwrite($targetHandle, $data);
249
		}
250 33
		$this->getState()->close($sourceHandle);
251 33
		return true;
252
	}
253
254
	/**
255
	 * Open a readable stream to a remote file
256
	 *
257
	 * @param string $source
258
	 * @return resource a read only stream with the contents of the remote file
259
	 *
260
	 * @throws \Icewind\SMB\Exception\NotFoundException
261
	 * @throws \Icewind\SMB\Exception\InvalidTypeException
262
	 */
263 29
	public function read($source) {
264 29
		$url = $this->buildUrl($source);
265 20
		$handle = $this->getState()->open($url, 'r');
266 20
		return NativeReadStream::wrap($this->getState(), $handle, 'r', $url);
267
	}
268
269
	/**
270
	 * Open a writeable stream to a remote file
271
	 * Note: This method will truncate the file to 0bytes first
272
	 *
273
	 * @param string $source
274
	 * @return resource a writeable stream
275
	 *
276
	 * @throws \Icewind\SMB\Exception\NotFoundException
277
	 * @throws \Icewind\SMB\Exception\InvalidTypeException
278
	 */
279 29
	public function write($source) {
280 29
		$url = $this->buildUrl($source);
281 20
		$handle = $this->getState()->create($url);
282 20
		return NativeWriteStream::wrap($this->getState(), $handle, 'w', $url);
283
	}
284
285
	/**
286
	 * Open a writeable stream and set the cursor to the end of the stream
287
	 *
288
	 * @param string $source
289
	 * @return resource a writeable stream
290
	 *
291
	 * @throws \Icewind\SMB\Exception\NotFoundException
292
	 * @throws \Icewind\SMB\Exception\InvalidTypeException
293
	 */
294 1
	public function append($source) {
295 1
		$url = $this->buildUrl($source);
296 1
		$handle = $this->getState()->open($url, "a+");
297 1
		return NativeWriteStream::wrap($this->getState(), $handle, "a", $url);
298
	}
299
300
	/**
301
	 * Get extended attributes for the path
302
	 *
303
	 * @param string $path
304
	 * @param string $attribute attribute to get the info
305
	 * @return string the attribute value
306
	 */
307 113
	public function getAttribute($path, $attribute) {
308 113
		return $this->getState()->getxattr($this->buildUrl($path), $attribute);
309
	}
310
311
	/**
312
	 * Set extended attributes for the given path
313
	 *
314
	 * @param string $path
315
	 * @param string $attribute attribute to get the info
316
	 * @param string|int $value
317
	 * @return mixed the attribute value
318
	 */
319 8
	public function setAttribute($path, $attribute, $value) {
320 8
		if ($attribute === 'system.dos_attr.mode' and is_int($value)) {
321 8
			$value = '0x' . dechex($value);
322
		}
323
324 8
		return $this->getState()->setxattr($this->buildUrl($path), $attribute, $value);
325
	}
326
327
	/**
328
	 * Set DOS comaptible node mode
329
	 *
330
	 * @param string $path
331
	 * @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL
332
	 * @return mixed
333
	 */
334 8
	public function setMode($path, $mode) {
335 8
		return $this->setAttribute($path, 'system.dos_attr.mode', $mode);
336
	}
337
338
	/**
339
	 * Start smb notify listener
340
	 * Note: This is a blocking call
341
	 *
342
	 * @param string $path
343
	 * @return INotifyHandler
344
	 */
345
	public function notify($path) {
346
		// php-smbclient does not support notify (https://github.com/eduardok/libsmbclient-php/issues/29)
347
		// so we use the smbclient based backend for this
348
		if (!Server::available($this->server->getSystem())) {
349
			throw new DependencyException('smbclient not found in path for notify command');
350
		}
351
		$share = new Share($this->server, $this->getName(), $this->server->getSystem());
352
		return $share->notify($path);
353
	}
354
355 254
	public function __destruct() {
356 254
		unset($this->state);
357 254
	}
358
}
359