Completed
Push — master ( ed2998...1bedfc )
by Morris
16:38
created

SMB::isUpdatable()   B

Complexity

Conditions 5
Paths 11

Size

Total Lines 12
Code Lines 8

Duplication

Lines 12
Ratio 100 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 11
nop 1
dl 12
loc 12
rs 8.8571
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\NotFoundException;
43
use Icewind\SMB\IFileInfo;
44
use Icewind\SMB\Native\NativeServer;
45
use Icewind\SMB\ServerFactory;
46
use Icewind\SMB\System;
47
use Icewind\Streams\CallbackWrapper;
48
use Icewind\Streams\IteratorDirectory;
49
use OC\Cache\CappedMemoryCache;
50
use OC\Files\Filesystem;
51
use OC\Files\Storage\Common;
52
use OCA\Files_External\Lib\Notify\SMBNotifyHandler;
53
use OCP\Files\Notify\IChange;
54
use OCP\Files\Notify\IRenameChange;
55
use OCP\Files\Storage\INotifyStorage;
56
use OCP\Files\StorageNotAvailableException;
57
use OCP\ILogger;
58
59
class SMB extends Common implements INotifyStorage {
60
	/**
61
	 * @var \Icewind\SMB\IServer
62
	 */
63
	protected $server;
64
65
	/**
66
	 * @var \Icewind\SMB\IShare
67
	 */
68
	protected $share;
69
70
	/**
71
	 * @var string
72
	 */
73
	protected $root;
74
75
	/**
76
	 * @var \Icewind\SMB\IFileInfo[]
77
	 */
78
	protected $statCache;
79
80
	public function __construct($params) {
81
		if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
82
			list($workgroup, $user) = $this->splitUser($params['user']);
83
			$auth = new BasicAuth($user, $workgroup, $params['password']);
84
			$serverFactory = new ServerFactory();
85
			$this->server = $serverFactory->createServer($params['host'], $auth);
86
			$this->share = $this->server->getShare(trim($params['share'], '/'));
87
88
			$this->root = $params['root'] ?? '/';
89
			$this->root = '/' . ltrim($this->root, '/');
90
			$this->root = rtrim($this->root, '/') . '/';
91
		} else {
92
			throw new \Exception('Invalid configuration');
93
		}
94
		$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...
95
		parent::__construct($params);
96
	}
97
98
	private function splitUser($user) {
99
		if (strpos($user, '/')) {
100
			return explode('/', $user, 2);
101
		} elseif (strpos($user, '\\')) {
102
			return explode('\\', $user);
103
		} else {
104
			return [null, $user];
105
		}
106
	}
107
108
	/**
109
	 * @return string
110
	 */
111
	public function getId() {
112
		// FIXME: double slash to keep compatible with the old storage ids,
113
		// failure to do so will lead to creation of a new storage id and
114
		// loss of shares from the storage
115
		return 'smb::' . $this->server->getAuth()->getUsername() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
116
	}
117
118
	/**
119
	 * @param string $path
120
	 * @return string
121
	 */
122
	protected function buildPath($path) {
123
		return Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
124
	}
125
126 View Code Duplication
	protected function relativePath($fullPath) {
127
		if ($fullPath === $this->root) {
128
			return '';
129
		} else if (substr($fullPath, 0, strlen($this->root)) === $this->root) {
130
			return substr($fullPath, strlen($this->root));
131
		} else {
132
			return null;
133
		}
134
	}
135
136
	/**
137
	 * @param string $path
138
	 * @return \Icewind\SMB\IFileInfo
139
	 * @throws StorageNotAvailableException
140
	 */
141
	protected function getFileInfo($path) {
142
		try {
143
			$path = $this->buildPath($path);
144
			if (!isset($this->statCache[$path])) {
145
				$this->statCache[$path] = $this->share->stat($path);
146
			}
147
			return $this->statCache[$path];
148
		} 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...
149
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
150
		}
151
	}
152
153
	/**
154
	 * @param string $path
155
	 * @return \Icewind\SMB\IFileInfo[]
156
	 * @throws StorageNotAvailableException
157
	 */
158
	protected function getFolderContents($path) {
159
		try {
160
			$path = $this->buildPath($path);
161
			$files = $this->share->dir($path);
162
			foreach ($files as $file) {
163
				$this->statCache[$path . '/' . $file->getName()] = $file;
164
			}
165
			return array_filter($files, function (IFileInfo $file) {
166
				try {
167
					return !$file->isHidden();
168
				} 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...
169
					return false;
170
				} 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...
171
					return false;
172
				}
173
			});
174
		} 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...
175
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
176
		}
177
	}
178
179
	/**
180
	 * @param \Icewind\SMB\IFileInfo $info
181
	 * @return array
182
	 */
183
	protected function formatInfo($info) {
184
		$result = [
185
			'size' => $info->getSize(),
186
			'mtime' => $info->getMTime(),
187
		];
188
		if ($info->isDirectory()) {
189
			$result['type'] = 'dir';
190
		} else {
191
			$result['type'] = 'file';
192
		}
193
		return $result;
194
	}
195
196
	/**
197
	 * Rename the files. If the source or the target is the root, the rename won't happen.
198
	 *
199
	 * @param string $source the old name of the path
200
	 * @param string $target the new name of the path
201
	 * @return bool true if the rename is successful, false otherwise
202
	 */
203
	public function rename($source, $target) {
204
		if ($this->isRootDir($source) || $this->isRootDir($target)) {
205
			return false;
206
		}
207
208
		$absoluteSource = $this->buildPath($source);
209
		$absoluteTarget = $this->buildPath($target);
210
		try {
211
			$result = $this->share->rename($absoluteSource, $absoluteTarget);
212
		} 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...
213
			$this->remove($target);
214
			$result = $this->share->rename($absoluteSource, $absoluteTarget);
215
		} catch (\Exception $e) {
216
			\OC::$server->getLogger()->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...
217
			return false;
218
		}
219
		unset($this->statCache[$absoluteSource], $this->statCache[$absoluteTarget]);
220
		return $result;
221
	}
222
223
	public function stat($path) {
224
		try {
225
			$result = $this->formatInfo($this->getFileInfo($path));
226
		} 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...
227
			return false;
228
		} 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...
229
			return false;
230
		}
231
		if ($this->remoteIsShare() && $this->isRootDir($path)) {
232
			$result['mtime'] = $this->shareMTime();
233
		}
234
		return $result;
235
	}
236
237
	/**
238
	 * get the best guess for the modification time of the share
239
	 *
240
	 * @return int
241
	 */
242
	private function shareMTime() {
243
		$highestMTime = 0;
244
		$files = $this->share->dir($this->root);
245
		foreach ($files as $fileInfo) {
246
			try {
247
				if ($fileInfo->getMTime() > $highestMTime) {
248
					$highestMTime = $fileInfo->getMTime();
249
				}
250
			} 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...
251
				// Ignore this, can happen on unavailable DFS shares
252
			}
253
		}
254
		return $highestMTime;
255
	}
256
257
	/**
258
	 * Check if the path is our root dir (not the smb one)
259
	 *
260
	 * @param string $path the path
261
	 * @return bool
262
	 */
263
	private function isRootDir($path) {
264
		return $path === '' || $path === '/' || $path === '.';
265
	}
266
267
	/**
268
	 * Check if our root points to a smb share
269
	 *
270
	 * @return bool true if our root points to a share false otherwise
271
	 */
272
	private function remoteIsShare() {
273
		return $this->share->getName() && (!$this->root || $this->root === '/');
274
	}
275
276
	/**
277
	 * @param string $path
278
	 * @return bool
279
	 */
280
	public function unlink($path) {
281
		if ($this->isRootDir($path)) {
282
			return false;
283
		}
284
285
		try {
286
			if ($this->is_dir($path)) {
287
				return $this->rmdir($path);
288
			} else {
289
				$path = $this->buildPath($path);
290
				unset($this->statCache[$path]);
291
				$this->share->del($path);
292
				return true;
293
			}
294
		} 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...
295
			return false;
296
		} 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...
297
			return false;
298
		} 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...
299
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
300
		}
301
	}
302
303
	/**
304
	 * check if a file or folder has been updated since $time
305
	 *
306
	 * @param string $path
307
	 * @param int $time
308
	 * @return bool
309
	 */
310
	public function hasUpdated($path, $time) {
311
		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...
312
			// mtime doesn't work for shares, but giving the nature of the backend,
313
			// doing a full update is still just fast enough
314
			return true;
315
		} else {
316
			$actualTime = $this->filemtime($path);
317
			return $actualTime > $time;
318
		}
319
	}
320
321
	/**
322
	 * @param string $path
323
	 * @param string $mode
324
	 * @return resource|false
325
	 */
326
	public function fopen($path, $mode) {
327
		$fullPath = $this->buildPath($path);
328
		try {
329
			switch ($mode) {
330
				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...
331
				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...
332
					if (!$this->file_exists($path)) {
333
						return false;
334
					}
335
					return $this->share->read($fullPath);
336
				case 'w':
337
				case 'wb':
338
					$source = $this->share->write($fullPath);
339
					return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) {
340
						unset($this->statCache[$fullPath]);
341
					});
342
				case 'a':
343
				case 'ab':
344
				case 'r+':
345
				case 'w+':
346
				case 'wb+':
347
				case 'a+':
348
				case 'x':
349
				case 'x+':
350
				case 'c':
351
				case 'c+':
352
					//emulate these
353
					if (strrpos($path, '.') !== false) {
354
						$ext = substr($path, strrpos($path, '.'));
355
					} else {
356
						$ext = '';
357
					}
358 View Code Duplication
					if ($this->file_exists($path)) {
359
						if (!$this->isUpdatable($path)) {
360
							return false;
361
						}
362
						$tmpFile = $this->getCachedFile($path);
363
					} else {
364
						if (!$this->isCreatable(dirname($path))) {
365
							return false;
366
						}
367
						$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
368
					}
369
					$source = fopen($tmpFile, $mode);
370
					$share = $this->share;
371
					return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
372
						unset($this->statCache[$fullPath]);
373
						$share->put($tmpFile, $fullPath);
374
						unlink($tmpFile);
375
					});
376
			}
377
			return false;
378
		} 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...
379
			return false;
380
		} 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...
381
			return false;
382
		} 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...
383
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
384
		}
385
	}
386
387
	public function rmdir($path) {
388
		if ($this->isRootDir($path)) {
389
			return false;
390
		}
391
392
		try {
393
			$this->statCache = array();
394
			$content = $this->share->dir($this->buildPath($path));
395
			foreach ($content as $file) {
396
				if ($file->isDirectory()) {
397
					$this->rmdir($path . '/' . $file->getName());
398
				} else {
399
					$this->share->del($file->getPath());
400
				}
401
			}
402
			$this->share->rmdir($this->buildPath($path));
403
			return true;
404
		} 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...
405
			return false;
406
		} 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...
407
			return false;
408
		} 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...
409
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
410
		}
411
	}
412
413 View Code Duplication
	public function touch($path, $time = null) {
414
		try {
415
			if (!$this->file_exists($path)) {
416
				$fh = $this->share->write($this->buildPath($path));
417
				fclose($fh);
418
				return true;
419
			}
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
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
423
		}
424
	}
425
426
	public function opendir($path) {
427
		try {
428
			$files = $this->getFolderContents($path);
429
		} 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...
430
			return false;
431
		} 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...
432
			return false;
433
		}
434
		$names = array_map(function ($info) {
435
			/** @var \Icewind\SMB\IFileInfo $info */
436
			return $info->getName();
437
		}, $files);
438
		return IteratorDirectory::wrap($names);
439
	}
440
441
	public function filetype($path) {
442
		try {
443
			return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
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
		}
449
	}
450
451 View Code Duplication
	public function mkdir($path) {
452
		$path = $this->buildPath($path);
453
		try {
454
			$this->share->mkdir($path);
455
			return true;
456
		} 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...
457
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
458
		} 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...
459
			return false;
460
		}
461
	}
462
463
	public function file_exists($path) {
464
		try {
465
			$this->getFileInfo($path);
466
			return true;
467
		} 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...
468
			return false;
469
		} 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...
470
			return false;
471
		} 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...
472
			throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e);
473
		}
474
	}
475
476 View Code Duplication
	public function isReadable($path) {
477
		try {
478
			$info = $this->getFileInfo($path);
479
			return !$info->isHidden();
480
		} 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...
481
			return false;
482
		} 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...
483
			return false;
484
		}
485
	}
486
487 View Code Duplication
	public function isUpdatable($path) {
488
		try {
489
			$info = $this->getFileInfo($path);
490
			// following windows behaviour for read-only folders: they can be written into
491
			// (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
492
			return !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
493
		} 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...
494
			return false;
495
		} 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...
496
			return false;
497
		}
498
	}
499
500 View Code Duplication
	public function isDeletable($path) {
501
		try {
502
			$info = $this->getFileInfo($path);
503
			return !$info->isHidden() && !$info->isReadOnly();
504
		} 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...
505
			return false;
506
		} 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...
507
			return false;
508
		}
509
	}
510
511
	/**
512
	 * check if smbclient is installed
513
	 */
514
	public static function checkDependencies() {
515
		return (
516
			(bool)\OC_Helper::findBinaryPath('smbclient')
517
			|| NativeServer::available(new System())
518
		) ? true : ['smbclient'];
519
	}
520
521
	/**
522
	 * Test a storage for availability
523
	 *
524
	 * @return bool
525
	 */
526
	public function test() {
527
		try {
528
			return parent::test();
529
		} 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...
530
			return false;
531
		}
532
	}
533
534
	public function listen($path, callable $callback) {
535
		$this->notify($path)->listen(function (IChange $change) use ($callback) {
536
			if ($change instanceof IRenameChange) {
537
				return $callback($change->getType(), $change->getPath(), $change->getTargetPath());
538
			} else {
539
				return $callback($change->getType(), $change->getPath());
540
			}
541
		});
542
	}
543
544
	public function notify($path) {
545
		$path = '/' . ltrim($path, '/');
546
		$shareNotifyHandler = $this->share->notify($this->buildPath($path));
547
		return new SMBNotifyHandler($shareNotifyHandler, $this->root);
548
	}
549
}
550