1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Copyright (c) 2014 Robin Appelman <[email protected]> |
4
|
|
|
* This file is licensed under the Licensed under the MIT license: |
5
|
|
|
* http://opensource.org/licenses/MIT |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace Icewind\SMB; |
9
|
|
|
|
10
|
|
|
use Icewind\SMB\Exception\InvalidPathException; |
11
|
|
|
use Icewind\SMB\Exception\InvalidResourceException; |
12
|
|
|
|
13
|
|
|
class NativeShare extends AbstractShare { |
14
|
|
|
/** |
15
|
|
|
* @var Server $server |
16
|
|
|
*/ |
17
|
|
|
private $server; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var string $name |
21
|
|
|
*/ |
22
|
|
|
private $name; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var \Icewind\SMB\NativeState $state |
26
|
|
|
*/ |
27
|
|
|
private $state; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @param Server $server |
31
|
|
|
* @param string $name |
32
|
|
|
*/ |
33
|
|
|
public function __construct($server, $name) { |
34
|
|
|
parent::__construct(); |
35
|
|
|
$this->server = $server; |
36
|
|
|
$this->name = $name; |
37
|
|
|
$this->state = new NativeState(); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @throws \Icewind\SMB\Exception\ConnectionException |
42
|
|
|
* @throws \Icewind\SMB\Exception\AuthenticationException |
43
|
|
|
* @throws \Icewind\SMB\Exception\InvalidHostException |
44
|
|
|
*/ |
45
|
|
|
protected function connect() { |
46
|
|
|
if ($this->state and $this->state instanceof NativeShare) { |
47
|
|
|
return; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
$this->state->init($this->server->getWorkgroup(), $this->server->getUser(), $this->server->getPassword()); |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Get the name of the share |
55
|
|
|
* |
56
|
|
|
* @return string |
57
|
|
|
*/ |
58
|
|
|
public function getName() { |
59
|
|
|
return $this->name; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
private function buildUrl($path) { |
63
|
|
|
$this->verifyPath($path); |
64
|
|
|
$url = sprintf('smb://%s/%s', $this->server->getHost(), $this->name); |
65
|
|
|
if ($path) { |
66
|
|
|
$path = trim($path, '/'); |
67
|
|
|
$url .= '/'; |
68
|
|
|
$url .= implode('/', array_map('rawurlencode', explode('/', $path))); |
69
|
|
|
} |
70
|
|
|
return $url; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* List the content of a remote folder |
75
|
|
|
* |
76
|
|
|
* @param string $path |
77
|
|
|
* @return \Icewind\SMB\IFileInfo[] |
78
|
|
|
* |
79
|
|
|
* @throws \Icewind\SMB\Exception\NotFoundException |
80
|
|
|
* @throws \Icewind\SMB\Exception\InvalidTypeException |
81
|
|
|
*/ |
82
|
|
|
public function dir($path) { |
83
|
|
|
$this->connect(); |
84
|
|
|
$files = array(); |
85
|
|
|
|
86
|
|
|
$dh = $this->state->opendir($this->buildUrl($path)); |
87
|
|
|
while ($file = $this->state->readdir($dh)) { |
88
|
|
|
$name = $file['name']; |
89
|
|
|
if ($name !== '.' and $name !== '..') { |
90
|
|
|
$files [] = new NativeFileInfo($this, $path . '/' . $name, $name); |
91
|
|
|
} |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
$this->state->closedir($dh); |
95
|
|
|
return $files; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @param string $path |
100
|
|
|
* @return \Icewind\SMB\IFileInfo[] |
101
|
|
|
*/ |
102
|
|
|
public function stat($path) { |
103
|
|
|
return new NativeFileInfo($this, $path, basename($path), $this->getStat($path)); |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
public function getStat($path) { |
107
|
|
|
$this->connect(); |
108
|
|
|
return $this->state->stat($this->buildUrl($path)); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Create a folder on the share |
113
|
|
|
* |
114
|
|
|
* @param string $path |
115
|
|
|
* @return bool |
116
|
|
|
* |
117
|
|
|
* @throws \Icewind\SMB\Exception\NotFoundException |
118
|
|
|
* @throws \Icewind\SMB\Exception\AlreadyExistsException |
119
|
|
|
*/ |
120
|
|
|
public function mkdir($path) { |
121
|
|
|
$this->connect(); |
122
|
|
|
return $this->state->mkdir($this->buildUrl($path)); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Remove a folder on the share |
127
|
|
|
* |
128
|
|
|
* @param string $path |
129
|
|
|
* @return bool |
130
|
|
|
* |
131
|
|
|
* @throws \Icewind\SMB\Exception\NotFoundException |
132
|
|
|
* @throws \Icewind\SMB\Exception\InvalidTypeException |
133
|
|
|
*/ |
134
|
|
|
public function rmdir($path) { |
135
|
|
|
$this->connect(); |
136
|
|
|
return $this->state->rmdir($this->buildUrl($path)); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Delete a file on the share |
141
|
|
|
* |
142
|
|
|
* @param string $path |
143
|
|
|
* @return bool |
144
|
|
|
* |
145
|
|
|
* @throws \Icewind\SMB\Exception\NotFoundException |
146
|
|
|
* @throws \Icewind\SMB\Exception\InvalidTypeException |
147
|
|
|
*/ |
148
|
|
|
public function del($path) { |
149
|
|
|
$this->connect(); |
150
|
|
|
return $this->state->unlink($this->buildUrl($path)); |
151
|
|
|
} |
152
|
|
|
|
153
|
|
|
/** |
154
|
|
|
* Rename a remote file |
155
|
|
|
* |
156
|
|
|
* @param string $from |
157
|
|
|
* @param string $to |
158
|
|
|
* @return bool |
159
|
|
|
* |
160
|
|
|
* @throws \Icewind\SMB\Exception\NotFoundException |
161
|
|
|
* @throws \Icewind\SMB\Exception\AlreadyExistsException |
162
|
|
|
*/ |
163
|
|
|
public function rename($from, $to) { |
164
|
|
|
$this->connect(); |
165
|
|
|
return $this->state->rename($this->buildUrl($from), $this->buildUrl($to)); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Upload a local file |
170
|
|
|
* |
171
|
|
|
* @param string $source local file |
172
|
|
|
* @param string $target remove file |
173
|
|
|
* @return bool |
174
|
|
|
* |
175
|
|
|
* @throws \Icewind\SMB\Exception\NotFoundException |
176
|
|
|
* @throws \Icewind\SMB\Exception\InvalidTypeException |
177
|
|
|
*/ |
178
|
|
|
public function put($source, $target) { |
179
|
|
|
$this->connect(); |
180
|
|
|
$sourceHandle = fopen($source, 'rb'); |
181
|
|
|
$targetHandle = $this->state->create($this->buildUrl($target)); |
182
|
|
|
|
183
|
|
|
while ($data = fread($sourceHandle, 4096)) { |
184
|
|
|
$this->state->write($targetHandle, $data); |
185
|
|
|
} |
186
|
|
|
$this->state->close($targetHandle); |
187
|
|
|
return true; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Download a remote file |
192
|
|
|
* |
193
|
|
|
* @param string $source remove file |
194
|
|
|
* @param string $target local file |
195
|
|
|
* @return bool |
196
|
|
|
* |
197
|
|
|
* @throws \Icewind\SMB\Exception\NotFoundException |
198
|
|
|
* @throws \Icewind\SMB\Exception\InvalidTypeException |
199
|
|
|
* @throws \Icewind\SMB\Exception\InvalidPathException |
200
|
|
|
* @throws \Icewind\SMB\Exception\InvalidResourceException |
201
|
|
|
*/ |
202
|
|
|
public function get($source, $target) { |
203
|
|
|
if (!$target) { |
204
|
|
|
throw new InvalidPathException('Invalid target path: Filename cannot be empty'); |
205
|
|
|
} |
206
|
|
|
$targetHandle = @fopen($target, 'wb'); |
207
|
|
|
if (!$targetHandle) { |
208
|
|
|
$error = error_get_last(); |
209
|
|
|
if (is_array($error)) { |
210
|
|
|
$reason = $error['message']; |
211
|
|
|
} else { |
212
|
|
|
$reason = 'Unknown error'; |
213
|
|
|
} |
214
|
|
|
throw new InvalidResourceException('Failed opening local file "' . $target . '" for writing: ' . $reason); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
$this->connect(); |
218
|
|
|
$sourceHandle = $this->state->open($this->buildUrl($source), 'r'); |
219
|
|
|
if (!$sourceHandle) { |
220
|
|
|
fclose($targetHandle); |
221
|
|
|
throw new InvalidResourceException('Failed opening remote file "' . $source . '" for reading'); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
while ($data = $this->state->read($sourceHandle, 4096)) { |
225
|
|
|
fwrite($targetHandle, $data); |
226
|
|
|
} |
227
|
|
|
$this->state->close($sourceHandle); |
228
|
|
|
return true; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Open a readable stream top a remote file |
233
|
|
|
* |
234
|
|
|
* @param string $source |
235
|
|
|
* @return resource a read only stream with the contents of the remote file |
236
|
|
|
* |
237
|
|
|
* @throws \Icewind\SMB\Exception\NotFoundException |
238
|
|
|
* @throws \Icewind\SMB\Exception\InvalidTypeException |
239
|
|
|
*/ |
240
|
|
View Code Duplication |
public function read($source) { |
|
|
|
|
241
|
|
|
$this->connect(); |
242
|
|
|
$url = $this->buildUrl($source); |
243
|
|
|
$handle = $this->state->open($url, 'r'); |
244
|
|
|
return NativeStream::wrap($this->state, $handle, 'r', $url); |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Open a readable stream top a remote file |
249
|
|
|
* |
250
|
|
|
* @param string $source |
251
|
|
|
* @return resource a read only stream with the contents of the remote file |
252
|
|
|
* |
253
|
|
|
* @throws \Icewind\SMB\Exception\NotFoundException |
254
|
|
|
* @throws \Icewind\SMB\Exception\InvalidTypeException |
255
|
|
|
*/ |
256
|
|
View Code Duplication |
public function write($source) { |
|
|
|
|
257
|
|
|
$this->connect(); |
258
|
|
|
$url = $this->buildUrl($source); |
259
|
|
|
$handle = $this->state->create($url); |
260
|
|
|
return NativeStream::wrap($this->state, $handle, 'w', $url); |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Get extended attributes for the path |
265
|
|
|
* |
266
|
|
|
* @param string $path |
267
|
|
|
* @param string $attribute attribute to get the info |
268
|
|
|
* @return string the attribute value |
269
|
|
|
*/ |
270
|
|
|
public function getAttribute($path, $attribute) { |
271
|
|
|
$this->connect(); |
272
|
|
|
|
273
|
|
|
$result = $this->state->getxattr($this->buildUrl($path), $attribute); |
274
|
|
|
return $result; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Get extended attributes for the path |
279
|
|
|
* |
280
|
|
|
* @param string $path |
281
|
|
|
* @param string $attribute attribute to get the info |
282
|
|
|
* @param mixed $value |
283
|
|
|
* @return string the attribute value |
284
|
|
|
*/ |
285
|
|
|
public function setAttribute($path, $attribute, $value) { |
286
|
|
|
$this->connect(); |
287
|
|
|
|
288
|
|
|
if ($attribute === 'system.dos_attr.mode' and is_int($value)) { |
289
|
|
|
$value = '0x' . dechex($value); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
return $this->state->setxattr($this->buildUrl($path), $attribute, $value); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* @param string $path |
297
|
|
|
* @param int $mode a combination of FileInfo::MODE_READONLY, FileInfo::MODE_ARCHIVE, FileInfo::MODE_SYSTEM and FileInfo::MODE_HIDDEN, FileInfo::NORMAL |
298
|
|
|
* @return mixed |
299
|
|
|
*/ |
300
|
|
|
public function setMode($path, $mode) { |
301
|
|
|
return $this->setAttribute($path, 'system.dos_attr.mode', $mode); |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* @param string $path |
306
|
|
|
* @param callable $callback callable which will be called for each received change |
307
|
|
|
* @return mixed |
308
|
|
|
*/ |
309
|
|
|
public function notify($path, callable $callback) { |
310
|
|
|
throw new \Exception('not implemented'); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
public function __destruct() { |
314
|
|
|
unset($this->state); |
315
|
|
|
} |
316
|
|
|
} |
317
|
|
|
|
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.