Completed
Push — stable8.2 ( 66f1ca...821663 )
by Morris
55:04 queued 36s
created

SMB::getFolderContents()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 8
rs 9.4285
cc 2
eloc 6
nc 2
nop 1
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Jörn Friedrich Dreyer <[email protected]>
5
 * @author Michael Gapczynski <[email protected]>
6
 * @author Morris Jobke <[email protected]>
7
 * @author Philipp Kapfer <[email protected]>
8
 * @author Robin Appelman <[email protected]>
9
 * @author Robin McCorkell <[email protected]>
10
 * @author Thomas Müller <[email protected]>
11
 * @author Vincent Petry <[email protected]>
12
 *
13
 * @copyright Copyright (c) 2015, ownCloud, Inc.
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OC\Files\Storage;
31
32
use Icewind\SMB\Exception\Exception;
33
use Icewind\SMB\Exception\NotFoundException;
34
use Icewind\SMB\NativeServer;
35
use Icewind\SMB\Server;
36
use Icewind\Streams\CallbackWrapper;
37
use Icewind\Streams\IteratorDirectory;
38
use OC\Cache\CappedMemoryCache;
39
use OC\Files\Filesystem;
40
41
class SMB extends Common {
42
	/**
43
	 * @var \Icewind\SMB\Server
44
	 */
45
	protected $server;
46
47
	/**
48
	 * @var \Icewind\SMB\Share
49
	 */
50
	protected $share;
51
52
	/**
53
	 * @var string
54
	 */
55
	protected $root;
56
57
	/**
58
	 * @var \Icewind\SMB\FileInfo[]
59
	 */
60
	protected $statCache;
61
62
	public function __construct($params) {
63
		if (isset($params['host']) && isset($params['user']) && isset($params['password']) && isset($params['share'])) {
64
			if (Server::NativeAvailable()) {
65
				$this->server = new NativeServer($params['host'], $params['user'], $params['password']);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Icewind\SMB\NativeS...], $params['password']) of type object<Icewind\SMB\NativeServer> is incompatible with the declared type object<Icewind\SMB\Server> of property $server.

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...
66
			} else {
67
				$this->server = new Server($params['host'], $params['user'], $params['password']);
68
			}
69
			$this->share = $this->server->getShare(trim($params['share'], '/'));
70
71
			$this->root = isset($params['root']) ? $params['root'] : '/';
72 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...
73
				$this->root = '/' . $this->root;
74
			}
75
			if (substr($this->root, -1, 1) != '/') {
76
				$this->root .= '/';
77
			}
78
		} else {
79
			throw new \Exception('Invalid configuration');
80
		}
81
		$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\FileInfo>> 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...
82
	}
83
84
	/**
85
	 * @return string
86
	 */
87
	public function getId() {
88
		// FIXME: double slash to keep compatible with the old storage ids,
89
		// failure to do so will lead to creation of a new storage id and
90
		// loss of shares from the storage
91
		return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
92
	}
93
94
	/**
95
	 * @param string $path
96
	 * @return string
97
	 */
98
	protected function buildPath($path) {
99
		return Filesystem::normalizePath($this->root . '/' . $path);
100
	}
101
102
	/**
103
	 * @param string $path
104
	 * @return \Icewind\SMB\IFileInfo
105
	 */
106
	protected function getFileInfo($path) {
107
		$path = $this->buildPath($path);
108
		if (!isset($this->statCache[$path])) {
109
			$this->statCache[$path] = $this->share->stat($path);
110
		}
111
		return $this->statCache[$path];
112
	}
113
114
	/**
115
	 * @param string $path
116
	 * @return \Icewind\SMB\IFileInfo[]
117
	 */
118
	protected function getFolderContents($path) {
119
		$path = $this->buildPath($path);
120
		$files = $this->share->dir($path);
121
		foreach ($files as $file) {
122
			$this->statCache[$path . '/' . $file->getName()] = $file;
123
		}
124
		return $files;
125
	}
126
127
	/**
128
	 * @param \Icewind\SMB\IFileInfo $info
129
	 * @return array
130
	 */
131
	protected function formatInfo($info) {
132
		return array(
133
			'size' => $info->getSize(),
134
			'mtime' => $info->getMTime()
135
		);
136
	}
137
138
	/**
139
	 * @param string $path
140
	 * @return array
141
	 */
142
	public function stat($path) {
143
		return $this->formatInfo($this->getFileInfo($path));
144
	}
145
146
	/**
147
	 * @param string $path
148
	 * @return bool
149
	 */
150
	public function unlink($path) {
151
		try {
152
			if ($this->is_dir($path)) {
153
				return $this->rmdir($path);
154
			} else {
155
				$path = $this->buildPath($path);
156
				unset($this->statCache[$path]);
157
				$this->share->del($path);
158
				return true;
159
			}
160
		} catch (NotFoundException $e) {
1 ignored issue
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...
161
			return false;
162
		}
163
	}
164
165
	/**
166
	 * check if a file or folder has been updated since $time
167
	 *
168
	 * @param string $path
169
	 * @param int $time
170
	 * @return bool
171
	 */
172
	public function hasUpdated($path, $time) {
173
		if (!$path and $this->root == '/') {
174
			// mtime doesn't work for shares, but giving the nature of the backend,
175
			// doing a full update is still just fast enough
176
			return true;
177
		} else {
178
			$actualTime = $this->filemtime($path);
179
			return $actualTime > $time;
180
		}
181
	}
182
183
	/**
184
	 * @param string $path
185
	 * @param string $mode
186
	 * @return resource
187
	 */
188
	public function fopen($path, $mode) {
189
		$fullPath = $this->buildPath($path);
190
		try {
191
			switch ($mode) {
192
				case 'r':
193
				case 'rb':
194
					if (!$this->file_exists($path)) {
195
						return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by OC\Files\Storage\SMB::fopen of type resource.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
196
					}
197
					return $this->share->read($fullPath);
198
				case 'w':
199
				case 'wb':
200
					return $this->share->write($fullPath);
201
				case 'a':
202
				case 'ab':
203
				case 'r+':
204
				case 'w+':
205
				case 'wb+':
206
				case 'a+':
207
				case 'x':
208
				case 'x+':
209
				case 'c':
210
				case 'c+':
211
					//emulate these
212
					if (strrpos($path, '.') !== false) {
213
						$ext = substr($path, strrpos($path, '.'));
214
					} else {
215
						$ext = '';
216
					}
217 View Code Duplication
					if ($this->file_exists($path)) {
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...
218
						if (!$this->isUpdatable($path)) {
219
							return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by OC\Files\Storage\SMB::fopen of type resource.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
220
						}
221
						$tmpFile = $this->getCachedFile($path);
222
					} else {
223
						if (!$this->isCreatable(dirname($path))) {
224
							return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by OC\Files\Storage\SMB::fopen of type resource.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
225
						}
226
						$tmpFile = \OCP\Files::tmpFile($ext);
227
					}
228
					$source = fopen($tmpFile, $mode);
229
					$share = $this->share;
230
					return CallBackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) {
231
						$share->put($tmpFile, $fullPath);
232
						unlink($tmpFile);
233
					});
234
			}
235
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by OC\Files\Storage\SMB::fopen of type resource.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
236
		} catch (NotFoundException $e) {
1 ignored issue
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...
237
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by OC\Files\Storage\SMB::fopen of type resource.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
238
		}
239
	}
240
241
	public function rmdir($path) {
242
		try {
243
			$this->statCache = array();
244
			$content = $this->share->dir($this->buildPath($path));
245
			foreach ($content as $file) {
246
				if ($file->isDirectory()) {
247
					$this->rmdir($path . '/' . $file->getName());
248
				} else {
249
					$this->share->del($file->getPath());
250
				}
251
			}
252
			$this->share->rmdir($this->buildPath($path));
253
			return true;
254
		} catch (NotFoundException $e) {
1 ignored issue
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...
255
			return false;
256
		}
257
	}
258
259
	public function touch($path, $time = null) {
260
		if (!$this->file_exists($path)) {
261
			$fh = $this->share->write($this->buildPath($path));
262
			fclose($fh);
263
			return true;
264
		}
265
		return false;
266
	}
267
268
	public function opendir($path) {
269
		$files = $this->getFolderContents($path);
270
		$names = array_map(function ($info) {
271
			/** @var \Icewind\SMB\IFileInfo $info */
272
			return $info->getName();
273
		}, $files);
274
		return IteratorDirectory::wrap($names);
275
	}
276
277
	public function filetype($path) {
278
		try {
279
			return $this->getFileInfo($path)->isDirectory() ? 'dir' : 'file';
280
		} catch (NotFoundException $e) {
1 ignored issue
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...
281
			return false;
282
		}
283
	}
284
285
	public function mkdir($path) {
286
		$path = $this->buildPath($path);
287
		try {
288
			$this->share->mkdir($path);
289
			return true;
290
		} catch (Exception $e) {
1 ignored issue
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...
291
			return false;
292
		}
293
	}
294
295
	public function file_exists($path) {
296
		try {
297
			$this->getFileInfo($path);
298
			return true;
299
		} catch (NotFoundException $e) {
1 ignored issue
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...
300
			return false;
301
		}
302
	}
303
304
	/**
305
	 * check if smbclient is installed
306
	 */
307
	public static function checkDependencies() {
308
		return (
309
			(bool)\OC_Helper::findBinaryPath('smbclient')
310
			|| Server::NativeAvailable()
311
		) ? true : ['smbclient'];
312
	}
313
}
314