Completed
Push — master ( 9cf11b...1c13e7 )
by Morris
88:12 queued 65:56
created

SMB::getFolderContents()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 8
nop 1
dl 0
loc 26
rs 8.8817
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Jesús Macias <[email protected]>
7
 * @author Joas Schilling <[email protected]>
8
 * @author Juan Pablo Villafañez <[email protected]>
9
 * @author Juan Pablo Villafáñez <[email protected]>
10
 * @author Jörn Friedrich Dreyer <[email protected]>
11
 * @author Michael Gapczynski <[email protected]>
12
 * @author Morris Jobke <[email protected]>
13
 * @author Philipp Kapfer <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Robin McCorkell <[email protected]>
16
 * @author Thomas Müller <[email protected]>
17
 * @author Vincent Petry <[email protected]>
18
 *
19
 * @license AGPL-3.0
20
 *
21
 * This code is free software: you can redistribute it and/or modify
22
 * it under the terms of the GNU Affero General Public License, version 3,
23
 * as published by the Free Software Foundation.
24
 *
25
 * This program is distributed in the hope that it will be useful,
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28
 * GNU Affero General Public License for more details.
29
 *
30
 * You should have received a copy of the GNU Affero General Public License, version 3,
31
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
32
 *
33
 */
34
35
namespace OCA\Files_External\Lib\Storage;
36
37
use Icewind\SMB\BasicAuth;
38
use Icewind\SMB\Exception\AlreadyExistsException;
39
use Icewind\SMB\Exception\ConnectException;
40
use Icewind\SMB\Exception\Exception;
41
use Icewind\SMB\Exception\ForbiddenException;
42
use Icewind\SMB\Exception\InvalidArgumentException;
43
use Icewind\SMB\Exception\NotFoundException;
44
use Icewind\SMB\IFileInfo;
45
use Icewind\SMB\Native\NativeServer;
46
use Icewind\SMB\ServerFactory;
47
use Icewind\SMB\System;
48
use Icewind\Streams\CallbackWrapper;
49
use Icewind\Streams\IteratorDirectory;
50
use OC\Cache\CappedMemoryCache;
51
use OC\Files\Filesystem;
52
use OC\Files\Storage\Common;
53
use OCA\Files_External\Lib\Notify\SMBNotifyHandler;
54
use OCP\Files\Notify\IChange;
55
use OCP\Files\Notify\IRenameChange;
56
use OCP\Files\Storage\INotifyStorage;
57
use OCP\Files\StorageNotAvailableException;
58
use OCP\ILogger;
59
60
class SMB extends Common implements INotifyStorage {
61
	/**
62
	 * @var \Icewind\SMB\IServer
63
	 */
64
	protected $server;
65
66
	/**
67
	 * @var \Icewind\SMB\IShare
68
	 */
69
	protected $share;
70
71
	/**
72
	 * @var string
73
	 */
74
	protected $root;
75
76
	/**
77
	 * @var \Icewind\SMB\IFileInfo[]
78
	 */
79
	protected $statCache;
80
81
	/** @var ILogger */
82
	protected $logger;
83
84
	public function __construct($params) {
85
		if (!isset($params['host'])) {
86
			throw new \Exception('Invalid configuration, no host provided');
87
		}
88
89
		if (isset($params['auth'])) {
90
			$auth = $params['auth'];
91
		} else if (isset($params['user']) && isset($params['password']) && isset($params['share'])) {
92
			list($workgroup, $user) = $this->splitUser($params['user']);
93
			$auth = new BasicAuth($user, $workgroup, $params['password']);
94
		} else {
95
			throw new \Exception('Invalid configuration, no credentials provided');
96
		}
97
98
		if (isset($params['logger'])) {
99
			$this->logger = $params['logger'];
100
		} else {
101
			$this->logger = \OC::$server->getLogger();
102
		}
103
104
		$serverFactory = new ServerFactory();
105
		$this->server = $serverFactory->createServer($params['host'], $auth);
106
		$this->share = $this->server->getShare(trim($params['share'], '/'));
107
108
		$this->root = $params['root'] ?? '/';
109
		$this->root = '/' . ltrim($this->root, '/');
110
		$this->root = rtrim($this->root, '/') . '/';
111
112
		$this->statCache = new CappedMemoryCache();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \OC\Cache\CappedMemoryCache() of type object<OC\Cache\CappedMemoryCache> is incompatible with the declared type array<integer,object<Icewind\SMB\IFileInfo>> of property $statCache.

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...
113
		parent::__construct($params);
114
	}
115
116
	private function splitUser($user) {
117
		if (strpos($user, '/')) {
118
			return explode('/', $user, 2);
119
		} elseif (strpos($user, '\\')) {
120
			return explode('\\', $user);
121
		} else {
122
			return [null, $user];
123
		}
124
	}
125
126
	/**
127
	 * @return string
128
	 */
129
	public function getId() {
130
		// FIXME: double slash to keep compatible with the old storage ids,
131
		// failure to do so will lead to creation of a new storage id and
132
		// loss of shares from the storage
133
		return 'smb::' . $this->server->getAuth()->getUsername() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
134
	}
135
136
	/**
137
	 * @param string $path
138
	 * @return string
139
	 */
140
	protected function buildPath($path) {
141
		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
142
	}
143
144 View Code Duplication
	protected function relativePath($fullPath) {
145
		if ($fullPath === $this->root) {
146
			return '';
147
		} else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
148
			return substr($fullPath, strlen($this->root));
149
		} else {
150
			return null;
151
		}
152
	}
153
154
	/**
155
	 * @param string $path
156
	 * @return \Icewind\SMB\IFileInfo
157
	 * @throws StorageNotAvailableException
158
	 */
159
	protected function getFileInfo($path) {
160
		try {
161
			$path = $this->buildPath($path);
162
			if (!isset($this->statCache[$path])) {
163
				$this->statCache[$path] = $this->share->stat($path);
164
			}
165
			return $this->statCache[$path];
166
		} catch (ConnectException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ConnectException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
167
			$this->logger->logException($e, ['message' => 'Error while getting file info']);
168
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
169
		}
170
	}
171
172
	/**
173
	 * @param string $path
174
	 * @return \Icewind\SMB\IFileInfo[]
175
	 * @throws StorageNotAvailableException
176
	 */
177
	protected function getFolderContents($path) {
178
		try {
179
			$path = $this->buildPath($path);
180
			$files = $this->share->dir($path);
181
			foreach ($files as $file) {
182
				$this->statCache[$path . '/' . $file->getName()] = $file;
183
			}
184
			return array_filter($files, function (IFileInfo $file) {
185
				try {
186
					if ($file->isHidden()) {
187
						$this->logger->debug('hiding hidden file ' . $file->getName());
188
					}
189
					return !$file->isHidden();
190
				} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
191
					$this->logger->logException($e, ['level' => ILogger::DEBUG, 'message' => 'Hiding forbidden entry ' . $file->getName()]);
192
					return false;
193
				} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
194
					$this->logger->logException($e, ['level' => ILogger::DEBUG, 'message' => 'Hiding not found entry ' . $file->getName()]);
195
					return false;
196
				}
197
			});
198
		} catch (ConnectException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ConnectException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
199
			$this->logger->logException($e, ['message' => 'Error while getting folder content']);
200
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
201
		}
202
	}
203
204
	/**
205
	 * @param \Icewind\SMB\IFileInfo $info
206
	 * @return array
207
	 */
208
	protected function formatInfo($info) {
209
		$result = [
210
			'size' => $info->getSize(),
211
			'mtime' => $info->getMTime(),
212
		];
213
		if ($info->isDirectory()) {
214
			$result['type'] = 'dir';
215
		} else {
216
			$result['type'] = 'file';
217
		}
218
		return $result;
219
	}
220
221
	/**
222
	 * Rename the files. If the source or the target is the root, the rename won't happen.
223
	 *
224
	 * @param string $source the old name of the path
225
	 * @param string $target the new name of the path
226
	 * @return bool true if the rename is successful, false otherwise
227
	 */
228
	public function rename($source, $target, $retry = true) {
229
		if ($this->isRootDir($source) || $this->isRootDir($target)) {
230
			return false;
231
		}
232
233
		$absoluteSource = $this->buildPath($source);
234
		$absoluteTarget = $this->buildPath($target);
235
		try {
236
			$result = $this->share->rename($absoluteSource, $absoluteTarget);
237
		} catch (AlreadyExistsException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\AlreadyExistsException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
238 View Code Duplication
			if ($retry) {
239
				$this->remove($target);
240
				$result = $this->share->rename($absoluteSource, $absoluteTarget, false);
241
			} else {
242
				$this->logger->logException($e, ['level' => ILogger::WARN]);
243
				return false;
244
			}
245
		} catch (InvalidArgumentException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\InvalidArgumentException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
246 View Code Duplication
			if ($retry) {
247
				$this->remove($target);
248
				$result = $this->share->rename($absoluteSource, $absoluteTarget, false);
249
			} else {
250
				$this->logger->logException($e, ['level' => ILogger::WARN]);
251
				return false;
252
			}
253
		} catch (\Exception $e) {
254
			$this->logger->logException($e, ['level' => ILogger::WARN]);
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
255
			return false;
256
		}
257
		unset($this->statCache[$absoluteSource], $this->statCache[$absoluteTarget]);
258
		return $result;
259
	}
260
261
	public function stat($path) {
262
		try {
263
			$result = $this->formatInfo($this->getFileInfo($path));
264
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
265
			return false;
266
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
267
			return false;
268
		}
269
		if ($this->remoteIsShare() && $this->isRootDir($path)) {
270
			$result['mtime'] = $this->shareMTime();
271
		}
272
		return $result;
273
	}
274
275
	/**
276
	 * get the best guess for the modification time of the share
277
	 *
278
	 * @return int
279
	 */
280
	private function shareMTime() {
281
		$highestMTime = 0;
282
		$files = $this->share->dir($this->root);
283
		foreach ($files as $fileInfo) {
284
			try {
285
				if ($fileInfo->getMTime() > $highestMTime) {
286
					$highestMTime = $fileInfo->getMTime();
287
				}
288
			} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
289
				// Ignore this, can happen on unavailable DFS shares
290
			}
291
		}
292
		return $highestMTime;
293
	}
294
295
	/**
296
	 * Check if the path is our root dir (not the smb one)
297
	 *
298
	 * @param string $path the path
299
	 * @return bool
300
	 */
301
	private function isRootDir($path) {
302
		return $path === '' || $path === '/' || $path === '.';
303
	}
304
305
	/**
306
	 * Check if our root points to a smb share
307
	 *
308
	 * @return bool true if our root points to a share false otherwise
309
	 */
310
	private function remoteIsShare() {
311
		return $this->share->getName() && (!$this->root || $this->root === '/');
312
	}
313
314
	/**
315
	 * @param string $path
316
	 * @return bool
317
	 */
318
	public function unlink($path) {
319
		if ($this->isRootDir($path)) {
320
			return false;
321
		}
322
323
		try {
324
			if ($this->is_dir($path)) {
325
				return $this->rmdir($path);
326
			} else {
327
				$path = $this->buildPath($path);
328
				unset($this->statCache[$path]);
329
				$this->share->del($path);
330
				return true;
331
			}
332
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
333
			return false;
334
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
335
			return false;
336
		} catch (ConnectException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ConnectException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
337
			$this->logger->logException($e, ['message' => 'Error while deleting file']);
338
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
339
		}
340
	}
341
342
	/**
343
	 * check if a file or folder has been updated since $time
344
	 *
345
	 * @param string $path
346
	 * @param int $time
347
	 * @return bool
348
	 */
349
	public function hasUpdated($path, $time) {
350
		if (!$path and $this->root === '/') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
351
			// mtime doesn't work for shares, but giving the nature of the backend,
352
			// doing a full update is still just fast enough
353
			return true;
354
		} else {
355
			$actualTime = $this->filemtime($path);
356
			return $actualTime > $time;
357
		}
358
	}
359
360
	/**
361
	 * @param string $path
362
	 * @param string $mode
363
	 * @return resource|false
364
	 */
365
	public function fopen($path, $mode) {
366
		$fullPath = $this->buildPath($path);
367
		try {
368
			switch ($mode) {
369
				case 'r':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
370
				case 'rb':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
371
					if (!$this->file_exists($path)) {
372
						return false;
373
					}
374
					return $this->share->read($fullPath);
375
				case 'w':
376
				case 'wb':
377
					$source = $this->share->write($fullPath);
378
					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
379
						unset($this->statCache[$fullPath]);
380
					});
381
				case 'a':
382
				case 'ab':
383
				case 'r+':
384
				case 'w+':
385
				case 'wb+':
386
				case 'a+':
387
				case 'x':
388
				case 'x+':
389
				case 'c':
390
				case 'c+':
391
					//emulate these
392
					if (strrpos($path, '.') !== false) {
393
						$ext = substr($path, strrpos($path, '.'));
394
					} else {
395
						$ext = '';
396
					}
397 View Code Duplication
					if ($this->file_exists($path)) {
398
						if (!$this->isUpdatable($path)) {
399
							return false;
400
						}
401
						$tmpFile = $this->getCachedFile($path);
402
					} else {
403
						if (!$this->isCreatable(dirname($path))) {
404
							return false;
405
						}
406
						$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
407
					}
408
					$source = fopen($tmpFile, $mode);
409
					$share = $this->share;
410
					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
411
						unset($this->statCache[$fullPath]);
412
						$share->put($tmpFile, $fullPath);
413
						unlink($tmpFile);
414
					});
415
			}
416
			return false;
417
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
418
			return false;
419
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
420
			return false;
421
		} catch (ConnectException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ConnectException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
422
			$this->logger->logException($e, ['message' => 'Error while opening file']);
423
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
424
		}
425
	}
426
427
	public function rmdir($path) {
428
		if ($this->isRootDir($path)) {
429
			return false;
430
		}
431
432
		try {
433
			$this->statCache = array();
434
			$content = $this->share->dir($this->buildPath($path));
435
			foreach ($content as $file) {
436
				if ($file->isDirectory()) {
437
					$this->rmdir($path . '/' . $file->getName());
438
				} else {
439
					$this->share->del($file->getPath());
440
				}
441
			}
442
			$this->share->rmdir($this->buildPath($path));
443
			return true;
444
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
445
			return false;
446
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
447
			return false;
448
		} catch (ConnectException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ConnectException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
449
			$this->logger->logException($e, ['message' => 'Error while removing folder']);
450
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
451
		}
452
	}
453
454 View Code Duplication
	public function touch($path, $time = null) {
455
		try {
456
			if (!$this->file_exists($path)) {
457
				$fh = $this->share->write($this->buildPath($path));
458
				fclose($fh);
459
				return true;
460
			}
461
			return false;
462
		} catch (ConnectException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ConnectException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
463
			$this->logger->logException($e, ['message' => 'Error while creating file']);
464
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
465
		}
466
	}
467
468
	public function opendir($path) {
469
		try {
470
			$files = $this->getFolderContents($path);
471
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
472
			return false;
473
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
474
			return false;
475
		}
476
		$names = array_map(function ($info) {
477
			/** @var \Icewind\SMB\IFileInfo $info */
478
			return $info->getName();
479
		}, $files);
480
		return IteratorDirectory::wrap($names);
481
	}
482
483
	public function filetype($path) {
484
		try {
485
			return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
486
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
487
			return false;
488
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
489
			return false;
490
		}
491
	}
492
493 View Code Duplication
	public function mkdir($path) {
494
		$path = $this->buildPath($path);
495
		try {
496
			$this->share->mkdir($path);
497
			return true;
498
		} catch (ConnectException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ConnectException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
499
			$this->logger->logException($e, ['message' => 'Error while creating folder']);
500
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
501
		} catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
502
			return false;
503
		}
504
	}
505
506
	public function file_exists($path) {
507
		try {
508
			$this->getFileInfo($path);
509
			return true;
510
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
511
			return false;
512
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
513
			return false;
514
		} catch (ConnectException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ConnectException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
515
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
516
		}
517
	}
518
519 View Code Duplication
	public function isReadable($path) {
520
		try {
521
			$info = $this->getFileInfo($path);
522
			return !$info->isHidden();
523
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
524
			return false;
525
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
526
			return false;
527
		}
528
	}
529
530 View Code Duplication
	public function isUpdatable($path) {
531
		try {
532
			$info = $this->getFileInfo($path);
533
			// following windows behaviour for read-only folders: they can be written into
534
			// (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
535
			return !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
536
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
537
			return false;
538
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
539
			return false;
540
		}
541
	}
542
543 View Code Duplication
	public function isDeletable($path) {
544
		try {
545
			$info = $this->getFileInfo($path);
546
			return !$info->isHidden() && !$info->isReadOnly();
547
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
548
			return false;
549
		} catch (ForbiddenException $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\ForbiddenException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
550
			return false;
551
		}
552
	}
553
554
	/**
555
	 * check if smbclient is installed
556
	 */
557
	public static function checkDependencies() {
558
		return (
559
			(bool)\OC_Helper::findBinaryPath('smbclient')
560
			|| NativeServer::available(new System())
561
		) ? true : ['smbclient'];
562
	}
563
564
	/**
565
	 * Test a storage for availability
566
	 *
567
	 * @return bool
568
	 */
569
	public function test() {
570
		try {
571
			return parent::test();
572
		} catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class Icewind\SMB\Exception\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
573
			$this->logger->logException($e);
574
			return false;
575
		}
576
	}
577
578
	public function listen($path, callable $callback) {
579
		$this->notify($path)->listen(function (IChange $change) use ($callback) {
580
			if ($change instanceof IRenameChange) {
581
				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
582
			} else {
583
				return $callback($change->getType(), $change->getPath());
584
			}
585
		});
586
	}
587
588
	public function notify($path) {
589
		$path = '/' . ltrim($path, '/');
590
		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
591
		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
592
	}
593
}
594