NativeShare::getName()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 1
cts 1
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
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\AlreadyExistsException;
12
use Icewind\SMB\Exception\AuthenticationException;
13
use Icewind\SMB\Exception\ConnectionException;
14
use Icewind\SMB\Exception\DependencyException;
15
use Icewind\SMB\Exception\InvalidHostException;
16
use Icewind\SMB\Exception\InvalidPathException;
17
use Icewind\SMB\Exception\InvalidResourceException;
18
use Icewind\SMB\Exception\InvalidTypeException;
19
use Icewind\SMB\Exception\NotFoundException;
20
use Icewind\SMB\IFileInfo;
21
use Icewind\SMB\INotifyHandler;
22
use Icewind\SMB\IServer;
23
use Icewind\SMB\Wrapped\Server;
24
use Icewind\SMB\Wrapped\Share;
25
26
class NativeShare extends AbstractShare {
27
	/**
28
	 * @var IServer $server
29
	 */
30
	private $server;
31
32
	/**
33
	 * @var string $name
34
	 */
35
	private $name;
36
37
	/** @var NativeState|null $state */
38
	private $state = null;
39 494
40 494
	public function __construct(IServer $server, string $name) {
41 494
		parent::__construct();
42 494
		$this->server = $server;
43 494
		$this->name = $name;
44
	}
45
46
	/**
47
	 * @throws ConnectionException
48
	 * @throws AuthenticationException
49
	 * @throws InvalidHostException
50 494
	 */
51 494
	protected function getState(): NativeState {
52 494
		if ($this->state) {
53
			return $this->state;
54
		}
55 494
56 494
		$this->state = new NativeState();
57 494
		$this->state->init($this->server->getAuth(), $this->server->getOptions());
58
		return $this->state;
59
	}
60
61
	/**
62
	 * Get the name of the share
63
	 *
64
	 * @return string
65 2
	 */
66 2
	public function getName(): string {
67
		return $this->name;
68
	}
69 494
70 494
	private function buildUrl(string $path): string {
71 494
		$this->verifyPath($path);
72 494
		$url = sprintf('smb://%s/%s', $this->server->getHost(), $this->name);
73 494
		if ($path) {
74 494
			$path = trim($path, '/');
75 494
			$url .= '/';
76
			$url .= implode('/', array_map('rawurlencode', explode('/', $path)));
77 494
		}
78
		return $url;
79
	}
80
81
	/**
82
	 * List the content of a remote folder
83
	 *
84
	 * @param string $path
85
	 * @return IFileInfo[]
86
	 *
87
	 * @throws NotFoundException
88
	 * @throws InvalidTypeException
89 494
	 */
90 494
	public function dir(string $path): array {
91
		$files = [];
92 494
93 494
		$dh = $this->getState()->opendir($this->buildUrl($path));
94 494
		while ($file = $this->getState()->readdir($dh, $path)) {
95 494
			$name = $file['name'];
96 186
			if ($name !== '.' and $name !== '..') {
97 186
				$fullPath = $path . '/' . $name;
98
				$files [] = new NativeFileInfo($this, $fullPath, $name);
99
			}
100
		}
101 494
102 494
		$this->getState()->closedir($dh, $path);
103
		return $files;
104
	}
105
106
	/**
107
	 * @param string $path
108
	 * @return IFileInfo
109 50
	 */
110 50
	public function stat(string $path): IFileInfo {
111
		$info = new NativeFileInfo($this, $path, self::mb_basename($path));
112
113 50
		// trigger attribute loading
114
		$info->getSize();
115 30
116
		return $info;
117
	}
118
119
	/**
120
	 * Multibyte unicode safe version of basename()
121
	 *
122
	 * @param string $path
123
	 * @link http://php.net/manual/en/function.basename.php#121405
124
	 * @return string
125 50
	 */
126 50
	protected static function mb_basename(string $path): string {
127 30
		if (preg_match('@^.*[\\\\/]([^\\\\/]+)$@s', $path, $matches)) {
128 20
			return $matches[1];
129 18
		} elseif (preg_match('@^([^\\\\/]+)$@s', $path, $matches)) {
130
			return $matches[1];
131
		}
132 2
133
		return '';
134
	}
135
136
	/**
137
	 * Create a folder on the share
138
	 *
139
	 * @param string $path
140
	 * @return bool
141
	 *
142
	 * @throws NotFoundException
143
	 * @throws AlreadyExistsException
144 494
	 */
145 494
	public function mkdir(string $path): bool {
146
		return $this->getState()->mkdir($this->buildUrl($path));
147
	}
148
149
	/**
150
	 * Remove a folder on the share
151
	 *
152
	 * @param string $path
153
	 * @return bool
154
	 *
155
	 * @throws NotFoundException
156
	 * @throws InvalidTypeException
157 494
	 */
158 494
	public function rmdir(string $path): bool {
159
		return $this->getState()->rmdir($this->buildUrl($path));
160
	}
161
162
	/**
163
	 * Delete a file on the share
164
	 *
165
	 * @param string $path
166
	 * @return bool
167
	 *
168
	 * @throws NotFoundException
169
	 * @throws InvalidTypeException
170 250
	 */
171 250
	public function del(string $path): bool {
172
		return $this->getState()->unlink($this->buildUrl($path));
173
	}
174
175
	/**
176
	 * Rename a remote file
177
	 *
178
	 * @param string $from
179
	 * @param string $to
180
	 * @return bool
181
	 *
182
	 * @throws NotFoundException
183
	 * @throws AlreadyExistsException
184 56
	 */
185 56
	public function rename(string $from, string $to): bool {
186
		return $this->getState()->rename($this->buildUrl($from), $this->buildUrl($to));
187
	}
188
189
	/**
190
	 * Upload a local file
191
	 *
192
	 * @param string $source local file
193
	 * @param string $target remove file
194
	 * @return bool
195
	 *
196
	 * @throws NotFoundException
197
	 * @throws InvalidTypeException
198 210
	 */
199 210
	public function put(string $source, string $target): bool {
200 210
		$sourceHandle = fopen($source, 'rb');
201
		$targetUrl = $this->buildUrl($target);
202 192
203
		$targetHandle = $this->getState()->create($targetUrl);
204 188
205 188
		while ($data = fread($sourceHandle, NativeReadStream::CHUNK_SIZE)) {
206
			$this->getState()->write($targetHandle, $data, $targetUrl);
207 188
		}
208 188
		$this->getState()->close($targetHandle, $targetUrl);
209
		return true;
210
	}
211
212
	/**
213
	 * Download a remote file
214
	 *
215
	 * @param string $source remove file
216
	 * @param string $target local file
217
	 * @return bool
218
	 *
219
	 * @throws AuthenticationException
220
	 * @throws ConnectionException
221
	 * @throws InvalidHostException
222
	 * @throws InvalidPathException
223 90
	 * @throws InvalidResourceException
224 90
	 */
225 18
	public function get(string $source, string $target): bool {
226
		if (!$target) {
227
			throw new InvalidPathException('Invalid target path: Filename cannot be empty');
228 72
		}
229 68
230
		$sourceHandle = $this->getState()->open($this->buildUrl($source), 'r');
231
232
		$targetHandle = @fopen($target, 'wb');
233 68
		if (!$targetHandle) {
234 68
			$error = error_get_last();
235 2
			if (is_array($error)) {
236 2
				$reason = $error['message'];
237 2
			} else {
238
				$reason = 'Unknown error';
239
			}
240
			$this->getState()->close($sourceHandle, $this->buildUrl($source));
241 2
			throw new InvalidResourceException('Failed opening local file "' . $target . '" for writing: ' . $reason);
242 2
		}
243
244
		while ($data = $this->getState()->read($sourceHandle, NativeReadStream::CHUNK_SIZE, $source)) {
245 66
			fwrite($targetHandle, $data);
246 66
		}
247
		$this->getState()->close($sourceHandle, $this->buildUrl($source));
248 66
		return true;
249 66
	}
250
251
	/**
252
	 * Open a readable stream to a remote file
253
	 *
254
	 * @param string $source
255
	 * @return resource a read only stream with the contents of the remote file
256
	 *
257
	 * @throws NotFoundException
258
	 * @throws InvalidTypeException
259
	 */
260
	public function read(string $source) {
261 58
		$url = $this->buildUrl($source);
262 58
		$handle = $this->getState()->open($url, 'r');
263 40
		return NativeReadStream::wrap($this->getState(), $handle, 'r', $url);
264 40
	}
265
266
	/**
267
	 * Open a writeable stream to a remote file
268
	 * Note: This method will truncate the file to 0bytes first
269
	 *
270
	 * @param string $source
271
	 * @return resource a writeable stream
272
	 *
273
	 * @throws NotFoundException
274
	 * @throws InvalidTypeException
275
	 */
276
	public function write(string $source) {
277 58
		$url = $this->buildUrl($source);
278 58
		$handle = $this->getState()->create($url);
279 40
		return NativeWriteStream::wrap($this->getState(), $handle, 'w', $url);
280 40
	}
281
282
	/**
283
	 * Open a writeable stream and set the cursor to the end of the stream
284
	 *
285
	 * @param string $source
286
	 * @return resource a writeable stream
287
	 *
288
	 * @throws NotFoundException
289
	 * @throws InvalidTypeException
290
	 */
291
	public function append(string $source) {
292 2
		$url = $this->buildUrl($source);
293 2
		$handle = $this->getState()->open($url, "a+");
294 2
		return NativeWriteStream::wrap($this->getState(), $handle, "a", $url);
295 2
	}
296
297
	/**
298
	 * Get extended attributes for the path
299
	 *
300
	 * @param string $path
301
	 * @param string $attribute attribute to get the info
302
	 * @return string the attribute value
303
	 */
304
	public function getAttribute(string $path, string $attribute): string {
305 210
		return $this->getState()->getxattr($this->buildUrl($path), $attribute);
306 210
	}
307
308
	/**
309
	 * Set extended attributes for the given path
310
	 *
311
	 * @param string $path
312
	 * @param string $attribute attribute to get the info
313
	 * @param string|int $value
314
	 * @return mixed the attribute value
315
	 */
316
	public function setAttribute(string $path, string $attribute, $value) {
317
		if (is_int($value)) {
318
			if ($attribute === 'system.dos_attr.mode') {
319
				$value = '0x' . dechex($value);
320
			} else {
321
				throw new \InvalidArgumentException("Invalid value for attribute");
322
			}
323
		}
324
325
		return $this->getState()->setxattr($this->buildUrl($path), $attribute, $value);
326
	}
327
328
	/**
329
	 * Set DOS comaptible node mode
330
	 *
331
	 * @param string $path
332
	 * @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL
333
	 * @return mixed
334
	 */
335
	public function setMode(string $path, int $mode) {
336
		return $this->setAttribute($path, 'system.dos_attr.mode', $mode);
337
	}
338
339
	/**
340
	 * Start smb notify listener
341
	 * Note: This is a blocking call
342
	 *
343
	 * @param string $path
344
	 * @return INotifyHandler
345
	 */
346
	public function notify(string $path): INotifyHandler {
347
		// php-smbclient does not support notify (https://github.com/eduardok/libsmbclient-php/issues/29)
348
		// so we use the smbclient based backend for this
349
		if (!Server::available($this->server->getSystem())) {
350
			throw new DependencyException('smbclient not found in path for notify command');
351
		}
352
		$share = new Share($this->server, $this->getName(), $this->server->getSystem());
353
		return $share->notify($path);
354
	}
355
356
	public function getServer(): IServer {
357 494
		return $this->server;
358 494
	}
359 494
360
	public function __destruct() {
361
		unset($this->state);
362
	}
363
}
364