Completed
Pull Request — master (#32044)
by Thomas
19:39
created

SMB::leave()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 7
nop 2
dl 0
loc 22
rs 8.6346
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Jesús Macias <[email protected]>
5
 * @author Jörn Friedrich Dreyer <[email protected]>
6
 * @author Juan Pablo Villafañez <[email protected]>
7
 * @author Michael Gapczynski <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Philipp Kapfer <[email protected]>
10
 * @author Robin Appelman <[email protected]>
11
 * @author Robin McCorkell <[email protected]>
12
 * @author Thomas Müller <[email protected]>
13
 * @author Vincent Petry <[email protected]>
14
 *
15
 * @copyright Copyright (c) 2018, ownCloud GmbH
16
 * @license AGPL-3.0
17
 *
18
 * This code is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License, version 3,
20
 * as published by the Free Software Foundation.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
 * GNU Affero General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Affero General Public License, version 3,
28
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
29
 *
30
 */
31
32
namespace OCA\Files_External\Lib\Storage;
33
34
use function GuzzleHttp\Psr7\copy_to_stream;
35
use function GuzzleHttp\Psr7\stream_for;
36
use Icewind\SMB\Exception\AlreadyExistsException;
37
use Icewind\SMB\Exception\ConnectException;
38
use Icewind\SMB\Exception\Exception;
39
use Icewind\SMB\Exception\ForbiddenException;
40
use Icewind\SMB\Exception\NotFoundException;
41
use Icewind\SMB\BasicAuth;
42
use Icewind\SMB\IFileInfo;
43
use Icewind\SMB\Native\NativeServer;
44
use Icewind\SMB\Wrapped\FileInfo;
45
use Icewind\SMB\ServerFactory;
46
use Icewind\SMB\System;
47
use Icewind\SMB\IShare;
48
use Icewind\Streams\CallbackWrapper;
49
use Icewind\Streams\IteratorDirectory;
50
use OC\Cache\CappedMemoryCache;
51
use OC\Files\Filesystem;
52
use OCP\Files\Cache\ICache;
53
use OCP\Files\Storage\StorageAdapter;
54
use OCP\Files\StorageNotAvailableException;
55
use OCP\Util;
56
use Psr\Http\Message\StreamInterface;
57
58
class SMB extends StorageAdapter {
59
	/**
60
	 * @var \Icewind\SMB\IServer
61
	 */
62
	protected $server;
63
64
	/**
65
	 * @var \Icewind\SMB\IShare
66
	 */
67
	protected $share;
68
69
	/**
70
	 * @var string
71
	 */
72
	protected $root;
73
74
	/**
75
	 * @var ICache
76
	 */
77
	protected $statCache;
78
79
	public function __construct($params) {
80
		$loggedParams = $params;
81
		// remove password from log if it is set
82
		if (!empty($loggedParams['password'])) {
83
			$loggedParams['password'] = '***removed***';
84
		}
85
		$this->log('enter: '.__FUNCTION__.'('.\json_encode($loggedParams).')');
86
87
		if (isset($params['host'], $params['user'], $params['password'], $params['share'])) {
88
			$auth = new BasicAuth($params['user'], '', $params['password']);
89
			$serverFactory = new ServerFactory();
90
			$this->server = $serverFactory->createServer($params['host'], $auth);
91
			$this->share = $this->server->getShare(\trim($params['share'], '/'));
92
93
			$shareClass = \get_class($this->share);
94
			$this->log("using $shareClass for the connection");
95
96
			$this->root = isset($params['root']) ? $params['root'] : '/';
97 View Code Duplication
			if (!$this->root || $this->root[0] != '/') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
98
				$this->root = '/' . $this->root;
99
			}
100
			if (\substr($this->root, -1, 1) != '/') {
101
				$this->root .= '/';
102
			}
103
		} else {
104
			$ex = new \Exception('Invalid configuration');
105
			$this->leave(__FUNCTION__, $ex);
106
			throw $ex;
107
		}
108
		$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 object<OCP\Files\Cache\ICache> 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...
109
		$this->log('leave: '.__FUNCTION__.', getId:'.$this->getId());
110
	}
111
112
	/**
113
	 * @return string
114
	 */
115
	public function getId() {
116
		// FIXME: double slash to keep compatible with the old storage ids,
117
		// failure to do so will lead to creation of a new storage id and
118
		// loss of shares from the storage
119
		return 'smb::' . $this->server->getAuth()->getUsername() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
120
	}
121
122
	/**
123
	 * @param string $path
124
	 * @return string
125
	 */
126
	protected function buildPath($path) {
127
		$this->log('enter: '.__FUNCTION__."($path)");
128
		$result = Filesystem::normalizePath($this->root . '/' . $path, true, false, true);
129
		return $this->leave(__FUNCTION__, $result);
130
	}
131
132
	/**
133
	 * @param string $path
134
	 * @return \Icewind\SMB\IFileInfo
135
	 * @throws StorageNotAvailableException
136
	 * @throws ForbiddenException
137
	 * @throws NotFoundException
138
	 */
139
	protected function getFileInfo($path) {
140
		$this->log('enter: '.__FUNCTION__."($path)");
141
		$path = $this->buildPath($path);
142
		if (!isset($this->statCache[$path])) {
143
			try {
144
				$this->log("stat fetching '$path'");
145
				try {
146
					$this->statCache[$path] = $this->share->stat($path);
147
				} catch (NotFoundException $e) {
148
					if ($this->share instanceof IShare) {
149
						// smbclient may have problems with the allinfo cmd
150
						$this->log("stat for '$path' failed, trying to read parent dir");
151
						$infos = $this->share->dir(\dirname($path));
152
						foreach ($infos as $fileInfo) {
153
							if ($fileInfo->getName() === \basename($path)) {
154
								$this->statCache[$path] = $fileInfo;
155
								break;
156
							}
157
						}
158
						if (empty($this->statCache[$path])) {
159
							$this->leave(__FUNCTION__, $e);
160
							throw $e;
161
						}
162
					} else {
163
						// trust the results of libsmb
164
						$this->leave(__FUNCTION__, $e);
165
						throw $e;
166
					}
167
				}
168
				if ($this->isRootDir($path) && $this->statCache[$path]->isHidden()) {
169
					$this->log("unhiding stat for '$path'");
170
					// make root never hidden, may happen when accessing a shared drive (mode is 22, archived and readonly - neither is true ... whatever)
171
					if ($this->statCache[$path]->isReadOnly()) {
172
						$mode = IFileInfo::MODE_DIRECTORY & IFileInfo::MODE_READONLY;
173
					} else {
174
						$mode = IFileInfo::MODE_DIRECTORY;
175
					}
176
					$this->statCache[$path] = new FileInfo($path, '', 0, $this->statCache[$path]->getMTime(), $mode);
177
				}
178
			} catch (ConnectException $e) {
179
				$ex = new StorageNotAvailableException(
180
						$e->getMessage(), $e->getCode(), $e);
181
				$this->leave(__FUNCTION__, $ex);
182
				throw $ex;
183
			} catch (ForbiddenException $e) {
184
				if ($this->remoteIsShare() && $this->isRootDir($path)) { //mtime may not work for share root
185
					$this->log("faking stat for forbidden '$path'");
186
					$this->statCache[$path] = new FileInfo($path, '', 0, $this->shareMTime(), IFileInfo::MODE_DIRECTORY);
187
				} else {
188
					$this->leave(__FUNCTION__, $e);
189
					throw $e;
190
				}
191
			}
192
		} else {
193
			$this->log("stat cache hit for '$path'");
194
		}
195
		$result = $this->statCache[$path];
196
		return $this->leave(__FUNCTION__, $result);
197
	}
198
199
	/**
200
	 * @param string $path
201
	 * @return \Icewind\SMB\IFileInfo[]
202
	 * @throws StorageNotAvailableException
203
	 */
204
	protected function getFolderContents($path) {
205
		$this->log('enter: '.__FUNCTION__."($path)");
206
		try {
207
			$path = $this->buildPath($path);
208
			$result = [];
209
			$children = $this->share->dir($path);
210
			foreach ($children as $fileInfo) {
211
				// check if the file is readable before adding it to the list
212
				// can't use "isReadable" function here, use smb internals instead
213
				try {
214
					if ($fileInfo->isHidden()) {
215
						$this->log("{$fileInfo->getName()} isn't readable, skipping", Util::DEBUG);
216
					} else {
217
						$result[] = $fileInfo;
218
						//remember entry so we can answer file_exists and filetype without a full stat
219
						$this->statCache[$path . '/' . $fileInfo->getName()] = $fileInfo;
220
					}
221
				} catch (NotFoundException $e) {
222
					$this->swallow(__FUNCTION__, $e);
223
				}
224
			}
225
		} catch (ConnectException $e) {
226
			$ex = new StorageNotAvailableException(
227
				$e->getMessage(), $e->getCode(), $e);
228
			$this->leave(__FUNCTION__, $ex);
229
			throw $ex;
230
		}
231
		return $this->leave(__FUNCTION__, $result);
232
	}
233
234
	/**
235
	 * @param \Icewind\SMB\IFileInfo $info
236
	 * @return array
237
	 */
238
	protected function formatInfo($info) {
239
		$result = [
240
			'size' => $info->getSize(),
241
			'mtime' => $info->getMTime(),
242
		];
243
		if ($info->isDirectory()) {
244
			$result['type'] = 'dir';
245
		} else {
246
			$result['type'] = 'file';
247
		}
248
		return $result;
249
	}
250
251
	/**
252
	 * Rename the files. If the source or the target is the root, the rename won't happen.
253
	 *
254
	 * @param string $source the old name of the path
255
	 * @param string $target the new name of the path
256
	 * @return bool true if the rename is successful, false otherwise
257
	 */
258
	public function rename($source, $target) {
259
		$this->log("enter: rename('$source', '$target')", Util::DEBUG);
260
261
		if ($this->isRootDir($source) || $this->isRootDir($target)) {
262
			$this->log("refusing to rename \"$source\" to \"$target\"");
263
			return $this->leave(__FUNCTION__, false);
264
		}
265
266
		try {
267
			$result = $this->share->rename($this->root . $source, $this->root . $target);
268
			$this->removeFromCache($this->root . $source);
269
			$this->removeFromCache($this->root . $target);
270
		} 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...
271
			$this->swallow(__FUNCTION__, $e);
272
			$this->unlink($target);
273
			$result = $this->share->rename($this->root . $source, $this->root . $target);
274
			$this->removeFromCache($this->root . $source);
275
			$this->removeFromCache($this->root . $target);
276
		} catch (Exception $e) {
277
			$this->swallow(__FUNCTION__, $e);
278
			// Icewind\SMB\Exception\Exception, not a plain exception
279
			if ($e->getCode() === 22) {
280
				$this->unlink($target);
281
				$result = $this->share->rename($this->root . $source, $this->root . $target);
282
				$this->removeFromCache($this->root . $source);
283
				$this->removeFromCache($this->root . $target);
284
			} else {
285
				$result = false;
286
			}
287
		} catch (\Exception $e) {
288
			$this->swallow(__FUNCTION__, $e);
289
			$result = false;
290
		}
291
		return $this->leave(__FUNCTION__, $result);
292
	}
293
294
	private function removeFromCache($path) {
295
		$path = \trim($path, '/');
296
		// TODO The CappedCache does not really clear by prefix. It just clears all.
297
		//$this->dirCache->clear($path);
298
		$this->statCache->clear($path);
299
		//$this->xattrCache->clear($path);
300
	}
301
	/**
302
	 * @param string $path
303
	 * @return array
304
	 */
305
	public function stat($path) {
306
		$this->log('enter: '.__FUNCTION__."($path)");
307
		try {
308
			$result = $this->formatInfo($this->getFileInfo($path));
309
		} catch (NotFoundException $e) {
310
			$this->swallow(__FUNCTION__, $e);
311
			$result = false;
312
		}
313
		return $this->leave(__FUNCTION__, $result);
314
	}
315
316
	/**
317
	 * get the best guess for the modification time of the share
318
	 * NOTE: modification times do not bubble up the directory tree, basically
319
	 * we are just guessing a time
320
	 *
321
	 * @return int the calculated mtime for the folder
322
	 */
323
	private function shareMTime() {
324
		$this->log('enter: '.__FUNCTION__, Util::DEBUG);
325
		$files = $this->share->dir($this->root);
326
		$result = 0;
327
		foreach ($files as $fileInfo) {
328
			if ($fileInfo->getMTime() > $result) {
329
				$result = $fileInfo->getMTime();
330
			}
331
		}
332
		return $this->leave(__FUNCTION__, $result);
333
	}
334
	/**
335
	 * Check if the path is our root dir (not the smb one)
336
	 *
337
	 * @param string $path the path
338
	 * @return bool true if it's root, false if not
339
	 */
340
	private function isRootDir($path) {
341
		$this->log('enter: '.__FUNCTION__."($path)", Util::DEBUG);
342
		$result = $path === '' || $path === '/' || $path === '.';
343
		return $this->leave(__FUNCTION__, $result);
344
	}
345
	/**
346
	 * Check if our root points to a smb share
347
	 *
348
	 * @return bool true if our root points to a share false otherwise
349
	 */
350
	private function remoteIsShare() {
351
		$this->log('enter: '.__FUNCTION__, Util::DEBUG);
352
		$result = $this->share->getName() && (!$this->root || $this->root === '/');
353
		return $this->leave(__FUNCTION__, $result);
354
	}
355
	/**
356
	 * @param string $path
357
	 * @return bool
358
	 * @throws StorageNotAvailableException
359
	 */
360
	public function unlink($path) {
361
		$this->log('enter: '.__FUNCTION__."($path)");
362
363
		if ($this->isRootDir($path)) {
364
			$this->log("refusing to unlink \"$path\"");
365
			return $this->leave(__FUNCTION__, false);
366
		}
367
368
		$result = false;
369
		try {
370
			if ($this->is_dir($path)) {
371
				$result = $this->rmdir($path);
372
			} else {
373
				$path = $this->buildPath($path);
374
				unset($this->statCache[$path]);
375
				$this->share->del($path);
376
				$result = true;
377
			}
378
		} catch (NotFoundException $e) {
379
			$this->swallow(__FUNCTION__, $e);
380
		} catch (ForbiddenException $e) {
381
			$this->swallow(__FUNCTION__, $e);
382
		} catch (ConnectException $e) {
383
			$ex = new StorageNotAvailableException(
384
				$e->getMessage(), $e->getCode(), $e);
385
			$this->leave(__FUNCTION__, $ex);
386
			throw $ex;
387
		}
388
		return $this->leave(__FUNCTION__, $result);
389
	}
390
391
	/**
392
	 * check if a file or folder has been updated since $time
393
	 *
394
	 * @param string $path
395
	 * @param int $time
396
	 * @return bool
397
	 */
398
	public function hasUpdated($path, $time) {
399
		$this->log('enter: '.__FUNCTION__."($path, $time)");
400
		$actualTime = $this->filemtime($path);
401
		$result = $actualTime > $time;
402
		return $this->leave(__FUNCTION__, $result);
403
	}
404
405
	/**
406
	 * @param string $path
407
	 * @param string $mode
408
	 * @return resource
409
	 * @throws StorageNotAvailableException
410
	 */
411
	public function fopen($path, $mode) {
412
		throw new \BadMethodCallException('fopen is no longer allowed to be called');
413
	}
414
415
	public function rmdir($path) {
416
		$this->log('enter: '.__FUNCTION__."($path)");
417
418
		if ($this->isRootDir($path)) {
419
			$this->log("refusing to delete \"$path\"");
420
			return $this->leave(__FUNCTION__, false);
421
		}
422
423
		$result = false;
424
		try {
425
			$this->removeFromCache($path);
426
			$content = $this->share->dir($this->buildPath($path));
427
			foreach ($content as $file) {
428
				if ($file->isDirectory()) {
429
					$this->rmdir($path . '/' . $file->getName());
430
				} else {
431
					$this->share->del($file->getPath());
432
				}
433
			}
434
			$this->share->rmdir($this->buildPath($path));
435
			$result = true;
436
		} catch (NotFoundException $e) {
437
			$this->swallow(__FUNCTION__, $e);
438
		} catch (ForbiddenException $e) {
439
			$this->swallow(__FUNCTION__, $e);
440
		} catch (ConnectException $e) {
441
			$ex = new StorageNotAvailableException(
442
				$e->getMessage(), $e->getCode(), $e);
443
			$this->leave(__FUNCTION__, $ex);
444
			throw $ex;
445
		}
446
		return $this->leave(__FUNCTION__, $result);
447
	}
448
449
	public function touch($path, $time = null) {
450
		$this->log('enter: '.__FUNCTION__."($path, $time)");
451
		try {
452
			if (!$this->file_exists($path)) {
453
				$fh = $this->share->write($this->buildPath($path));
454
				\fclose($fh);
455
				$result = true;
456
			} else {
457
				$result = false;
458
			}
459
		} catch (ConnectException $e) {
460
			$ex = new StorageNotAvailableException(
461
				$e->getMessage(), $e->getCode(), $e);
462
			$this->leave(__FUNCTION__, $ex);
463
			throw $ex;
464
		}
465
		return $this->leave(__FUNCTION__, $result);
466
	}
467
468
	public function opendir($path) {
469
		$this->log('enter: '.__FUNCTION__."($path)");
470
		$result = false;
471
		try {
472
			$files = $this->getFolderContents($path);
473
			$names = \array_map(function ($info) {
474
				/** @var \Icewind\SMB\IFileInfo $info */
475
				return $info->getName();
476
			}, $files);
477
			$result = IteratorDirectory::wrap($names);
478
		} catch (NotFoundException $e) {
479
			$this->swallow(__FUNCTION__, $e);
480
		} catch (ForbiddenException $e) {
481
			$this->swallow(__FUNCTION__, $e);
482
		}
483
		return $this->leave(__FUNCTION__, $result);
484
	}
485
486
	public function filetype($path) {
487
		$this->log('enter: '.__FUNCTION__."($path)");
488
		$result = false;
489
		try {
490
			$result = $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
491
		} catch (NotFoundException $e) {
492
			$this->swallow(__FUNCTION__, $e);
493
		} catch (ForbiddenException $e) {
494
			$this->swallow(__FUNCTION__, $e);
495
		}
496
		return $this->leave(__FUNCTION__, $result);
497
	}
498
499
	public function mkdir($path) {
500
		$this->log('enter: '.__FUNCTION__."($path)");
501
		$result = false;
502
		$path = $this->buildPath($path);
503
		try {
504
			$result = $this->share->mkdir($path);
505
		} catch (ConnectException $e) {
506
			$ex = new StorageNotAvailableException(
507
				$e->getMessage(), $e->getCode(), $e);
508
			$this->leave(__FUNCTION__, $ex);
509
			throw $ex;
510
		} catch (Exception $e) {
511
			$this->swallow(__FUNCTION__, $e);
512
		}
513
		return $this->leave(__FUNCTION__, $result);
514
	}
515
516
	public function file_exists($path) {
517
		$this->log('enter: '.__FUNCTION__."($path)");
518
		$result = false;
519
		try {
520
			$this->getFileInfo($path);
521
			$result = true;
522
		} catch (NotFoundException $e) {
523
			$this->swallow(__FUNCTION__, $e);
524
		} catch (ForbiddenException $e) {
525
			$this->swallow(__FUNCTION__, $e);
526
		} catch (ConnectException $e) {
527
			$ex = new StorageNotAvailableException(
528
				$e->getMessage(), $e->getCode(), $e);
529
			$this->leave(__FUNCTION__, $ex);
530
			throw $ex;
531
		}
532
		return $this->leave(__FUNCTION__, $result);
533
	}
534
535
	public function isReadable($path) {
536
		$this->log('enter: '.__FUNCTION__."($path)");
537
		$result = false;
538
		try {
539
			$info = $this->getFileInfo($path);
540
			$result = !$info->isHidden();
541
		} catch (NotFoundException $e) {
542
			$this->swallow(__FUNCTION__, $e);
543
		} catch (ForbiddenException $e) {
544
			$this->swallow(__FUNCTION__, $e);
545
		}
546
		return $this->leave(__FUNCTION__, $result);
547
	}
548
549 View Code Duplication
	public function isUpdatable($path) {
550
		$this->log('enter: '.__FUNCTION__."($path)");
551
		$result = false;
552
		try {
553
			$info = $this->getFileInfo($path);
554
			// following windows behaviour for read-only folders: they can be written into
555
			// (https://support.microsoft.com/en-us/kb/326549 - "cause" section)
556
			$result = !$info->isHidden() && (!$info->isReadOnly() || $this->is_dir($path));
557
		} catch (NotFoundException $e) {
558
			$this->swallow(__FUNCTION__, $e);
559
		} catch (ForbiddenException $e) {
560
			$this->swallow(__FUNCTION__, $e);
561
		}
562
		return $this->leave(__FUNCTION__, $result);
563
	}
564
565 View Code Duplication
	public function isDeletable($path) {
566
		$this->log('enter: '.__FUNCTION__."($path)");
567
		$result = false;
568
		try {
569
			$info = $this->getFileInfo($path);
570
			$result = !$info->isHidden() && !$info->isReadOnly();
571
		} catch (NotFoundException $e) {
572
			$this->swallow(__FUNCTION__, $e);
573
		} catch (ForbiddenException $e) {
574
			$this->swallow(__FUNCTION__, $e);
575
		}
576
		return $this->leave(__FUNCTION__, $result);
577
	}
578
579
	/**
580
	 * check if smbclient is installed
581
	 */
582
	public static function checkDependencies() {
583
		return (
584
			(bool)\OC_Helper::findBinaryPath('smbclient')
585
			|| NativeServer::available(new System())
586
		) ? true : ['smbclient'];
587
	}
588
589
	/**
590
	 * Test a storage for availability
591
	 *
592
	 * @return bool
593
	 */
594
	public function test() {
595
		$this->log('enter: '.__FUNCTION__."()");
596
		$result = false;
597
		try {
598
			$result = parent::test();
599
		} catch (Exception $e) {
600
			$this->swallow(__FUNCTION__, $e);
601
		}
602
		return $this->leave(__FUNCTION__, $result);
603
	}
604
605
	/**
606
	 * @param string $message
607
	 * @param int $level
608
	 * @param string $from
609
	 */
610
	private function log($message, $level = Util::DEBUG, $from = 'smb') {
611
		if (\OC::$server->getConfig()->getSystemValue('smb.logging.enable', false) === true) {
612
			Util::writeLog($from, $message, $level);
613
		}
614
	}
615
616
	/**
617
	 * if smb.logging.enable is set to true in the config will log a leave line
618
	 * with the given function, the return value or the exception
619
	 *
620
	 * @param $function
621
	 * @param mixed $result an exception will be logged and then returned
622
	 * @return mixed
623
	 */
624
	private function leave($function, $result) {
625
		if (\OC::$server->getConfig()->getSystemValue('smb.logging.enable', false) === false) {
626
			//don't bother building log strings
627
			return $result;
628
		} elseif ($result === true) {
629
			Util::writeLog('smb', "leave: $function, return true", Util::DEBUG);
630
		} elseif ($result === false) {
631
			Util::writeLog('smb', "leave: $function, return false", Util::DEBUG);
632
		} elseif (\is_string($result)) {
633
			Util::writeLog('smb', "leave: $function, return '$result'", Util::DEBUG);
634
		} elseif (\is_resource($result)) {
635
			Util::writeLog('smb', "leave: $function, return resource", Util::DEBUG);
636
		} elseif ($result instanceof \Exception) {
637
			Util::writeLog('smb', "leave: $function, throw ".\get_class($result)
638
				.' - code: '.$result->getCode()
639
				.' message: '.$result->getMessage()
640
				.' trace: '.$result->getTraceAsString(), Util::DEBUG);
641
		} else {
642
			Util::writeLog('smb', "leave: $function, return ".\json_encode($result, true), Util::DEBUG);
643
		}
644
		return $result;
645
	}
646
647
	private function swallow($function, \Exception $exception) {
648
		if (\OC::$server->getConfig()->getSystemValue('smb.logging.enable', false) === true) {
649
			Util::writeLog('smb', "$function swallowing ".\get_class($exception)
650
				.' - code: '.$exception->getCode()
651
				.' message: '.$exception->getMessage()
652
				.' trace: '.$exception->getTraceAsString(), Util::DEBUG);
653
		}
654
	}
655
656
	/**
657
	 * immediately close / free connection
658
	 */
659
	public function __destruct() {
660
		unset($this->share);
661
	}
662
663
	/**
664
	 * @param string $path
665
	 * @param array $options
666
	 * @return StreamInterface
667
	 * @since 11.0.0
668
	 */
669
	public function readFile(string $path, array $options = []): StreamInterface {
670
		$fullPath = $this->buildPath($path);
671
		if (!$this->file_exists($path)) {
672
			return stream_for($this->share->read($fullPath));
673
		}
674
		throw new NotFoundException();
675
	}
676
677
	/**
678
	 * @param string $path
679
	 * @param StreamInterface $stream
680
	 * @return int
681
	 * @since 11.0.0
682
	 */
683
	public function writeFile(string $path, StreamInterface $stream): int {
684
		$fullPath = $this->buildPath($path);
685
		$dest = $this->share->write($fullPath);
686
		copy_to_stream($stream, stream_for($dest));
687
		return $stream->getSize();
688
	}
689
}
690