1 | <?php |
||||||
2 | |||||||
3 | /** |
||||||
4 | * The FtpConnection class is a Simple FTP protocol implementation. |
||||||
5 | * |
||||||
6 | * @package ElkArte Forum |
||||||
7 | * @copyright ElkArte Forum contributors |
||||||
8 | * @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
||||||
9 | * |
||||||
10 | * This file contains code covered by: |
||||||
11 | * copyright: 2011 Simple Machines (http://www.simplemachines.org) |
||||||
12 | * |
||||||
13 | * @version 2.0 dev |
||||||
14 | * |
||||||
15 | */ |
||||||
16 | |||||||
17 | namespace ElkArte\Http; |
||||||
18 | |||||||
19 | use ElkArte\Helper\FileFunctions; |
||||||
20 | |||||||
21 | /** |
||||||
22 | * Simple FTP protocol implementation. |
||||||
23 | * |
||||||
24 | * https://www.faqs.org/rfcs/rfc959.html |
||||||
25 | */ |
||||||
26 | class FtpConnection |
||||||
27 | { |
||||||
28 | /** @var resource|string Holds the connection response */ |
||||||
29 | public $connection; |
||||||
30 | |||||||
31 | /** @var string|bool Holds any errors */ |
||||||
32 | public $error; |
||||||
33 | |||||||
34 | /** @var string Holds last message from the server */ |
||||||
35 | public $last_message; |
||||||
36 | |||||||
37 | /** @var array Passive connection */ |
||||||
38 | public $pasv; |
||||||
39 | |||||||
40 | /** @var string Holds last response message from the server */ |
||||||
41 | public $last_response; |
||||||
42 | |||||||
43 | /** |
||||||
44 | * Create a new FTP connection... |
||||||
45 | * |
||||||
46 | * @param string $ftp_server The server to connect to |
||||||
47 | * @param int $ftp_port The port to connect to |
||||||
48 | * @param string $ftp_user The username |
||||||
49 | * @param string $ftp_pass The password |
||||||
50 | */ |
||||||
51 | public function __construct($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = '[email protected]') |
||||||
52 | { |
||||||
53 | // Initialize variables. |
||||||
54 | $this->connection = 'no_connection'; |
||||||
55 | $this->error = false; |
||||||
56 | $this->pasv = []; |
||||||
57 | |||||||
58 | if ($ftp_server !== null) |
||||||
0 ignored issues
–
show
introduced
by
![]() |
|||||||
59 | { |
||||||
60 | $this->connect($ftp_server, $ftp_port, $ftp_user, $ftp_pass); |
||||||
61 | } |
||||||
62 | } |
||||||
63 | |||||||
64 | /** |
||||||
65 | * Connects to a server |
||||||
66 | * |
||||||
67 | * @param string $ftp_server The server to connect to |
||||||
68 | * @param int $ftp_port The port to connect to |
||||||
69 | * @param string $ftp_user The username |
||||||
70 | * @param string $ftp_pass The password |
||||||
71 | */ |
||||||
72 | public function connect($ftp_server, $ftp_port = 21, $ftp_user = 'anonymous', $ftp_pass = '[email protected]') |
||||||
73 | { |
||||||
74 | // Connect to the FTP server. |
||||||
75 | set_error_handler(static function () { /* ignore errors */ }); |
||||||
76 | $ftp_server = $this->getServer($ftp_server); |
||||||
77 | $this->connection = stream_socket_client($ftp_server . ':' . $ftp_port, $err_code, $err, 5); |
||||||
78 | restore_error_handler(); |
||||||
79 | if (!$this->connection || $err_code !== 0) |
||||||
80 | { |
||||||
81 | return $this->error = empty($err) ? 'bad_server' : $err; |
||||||
82 | } |
||||||
83 | |||||||
84 | // Get the welcome message... |
||||||
85 | if (!$this->check_response(220)) |
||||||
86 | { |
||||||
87 | $this->close(); |
||||||
88 | |||||||
89 | return $this->error = 'bad_response'; |
||||||
90 | } |
||||||
91 | |||||||
92 | // Send the username, it should ask for a password. |
||||||
93 | fwrite($this->connection, 'USER ' . $ftp_user . "\r\n"); |
||||||
94 | if (!$this->check_response(331)) |
||||||
95 | { |
||||||
96 | return $this->error = 'bad_username'; |
||||||
97 | } |
||||||
98 | |||||||
99 | // Now send the password... and hope it goes okay. |
||||||
100 | fwrite($this->connection, 'PASS ' . $ftp_pass . "\r\n"); |
||||||
101 | if (!$this->check_response(230)) |
||||||
102 | { |
||||||
103 | return $this->error = 'bad_password'; |
||||||
104 | } |
||||||
105 | |||||||
106 | return true; |
||||||
107 | } |
||||||
108 | |||||||
109 | /** |
||||||
110 | * Sanitize the supplied server string |
||||||
111 | * |
||||||
112 | * @param $ftp_server |
||||||
113 | * @return string |
||||||
114 | */ |
||||||
115 | public function getServer($ftp_server) |
||||||
116 | { |
||||||
117 | $location = parse_url($ftp_server); |
||||||
118 | $location['host'] = $location['host'] ?? $ftp_server; |
||||||
119 | $location['scheme'] = $location['scheme'] ?? ''; |
||||||
120 | |||||||
121 | $ftp_scheme = ''; |
||||||
122 | if ($location['scheme'] === 'ftps' || $location['scheme'] === 'https') |
||||||
123 | { |
||||||
124 | $ftp_scheme = 'ssl://'; |
||||||
125 | } |
||||||
126 | |||||||
127 | return $ftp_scheme . strtr($location['host'], array('/' => '', ':' => '', '@' => '')); |
||||||
128 | } |
||||||
129 | |||||||
130 | /** |
||||||
131 | * Reads the response to the command from the server |
||||||
132 | * |
||||||
133 | * @param string[]|string $desired string or array of acceptable return values |
||||||
134 | * |
||||||
135 | * @return bool |
||||||
136 | */ |
||||||
137 | public function check_response($desired) |
||||||
138 | { |
||||||
139 | $return_code = false; |
||||||
140 | $time = time(); |
||||||
141 | while (!$return_code && time() - $time < 4) |
||||||
142 | { |
||||||
143 | $this->last_message = fgets($this->connection, 1024); |
||||||
0 ignored issues
–
show
It seems like
$this->connection can also be of type string ; however, parameter $stream of fgets() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
144 | |||||||
145 | // A reply will start with a 3-digit code, followed by space " ", followed by one line of text |
||||||
146 | if (preg_match('~^(\d\d\d)\s(.+)$~m', $this->last_message, $matches) === 1) |
||||||
147 | { |
||||||
148 | $return_code = (int) $matches[1]; |
||||||
149 | $this->last_response = $return_code . ' :: ' . $matches[2]; |
||||||
150 | } |
||||||
151 | } |
||||||
152 | |||||||
153 | // Was the desired response returned? |
||||||
154 | return is_array($desired) ? in_array($return_code, $desired) : $return_code === $desired; |
||||||
155 | } |
||||||
156 | |||||||
157 | /** |
||||||
158 | * Changes to a directory (chdir) via the ftp connection |
||||||
159 | * |
||||||
160 | * @param string $ftp_path The path to the directory |
||||||
161 | * @return bool |
||||||
162 | */ |
||||||
163 | public function chdir($ftp_path) |
||||||
164 | { |
||||||
165 | if (!$this->hasConnection()) |
||||||
166 | { |
||||||
167 | return false; |
||||||
168 | } |
||||||
169 | |||||||
170 | // No slash on the end, please... |
||||||
171 | if ($ftp_path !== '/' && substr($ftp_path, -1) === '/') |
||||||
172 | { |
||||||
173 | $ftp_path = substr($ftp_path, 0, -1); |
||||||
174 | } |
||||||
175 | |||||||
176 | fwrite($this->connection, 'CWD ' . $ftp_path . "\r\n"); |
||||||
177 | if (!$this->check_response(250)) |
||||||
178 | { |
||||||
179 | $this->error = 'bad_path'; |
||||||
180 | |||||||
181 | return false; |
||||||
182 | } |
||||||
183 | |||||||
184 | return true; |
||||||
185 | } |
||||||
186 | |||||||
187 | /** |
||||||
188 | * Changes a files attributes (chmod) |
||||||
189 | * |
||||||
190 | * @param string $ftp_file The file to CHMOD |
||||||
191 | * @param int $chmod The value for the CHMOD operation |
||||||
192 | * @return bool If the chmod was successful or not |
||||||
193 | */ |
||||||
194 | public function chmod($ftp_file, $chmod) |
||||||
195 | { |
||||||
196 | if (!$this->hasConnection()) |
||||||
197 | { |
||||||
198 | return false; |
||||||
199 | } |
||||||
200 | |||||||
201 | if (trim($ftp_file) === '') |
||||||
202 | { |
||||||
203 | $ftp_file = '.'; |
||||||
204 | } |
||||||
205 | |||||||
206 | // Convert the chmod value from octal (0777) to text ("777"). |
||||||
207 | fwrite($this->connection, 'SITE CHMOD ' . decoct($chmod) . ' ' . $ftp_file . "\r\n"); |
||||||
208 | if (!$this->check_response(200)) |
||||||
209 | { |
||||||
210 | $this->error = $this->last_response; |
||||||
211 | |||||||
212 | return false; |
||||||
213 | } |
||||||
214 | |||||||
215 | return true; |
||||||
216 | } |
||||||
217 | |||||||
218 | /** |
||||||
219 | * Uses a supplied list of modes to make a file or directory writable |
||||||
220 | * assumes supplied name is relative from boarddir, which it should be |
||||||
221 | * |
||||||
222 | * @param string $ftp_file |
||||||
223 | * @param array|int $chmod |
||||||
224 | * @return bool |
||||||
225 | */ |
||||||
226 | public function ftp_chmod($ftp_file, $chmod) |
||||||
227 | { |
||||||
228 | $chmod = is_array($chmod) ? $chmod : (array) $chmod; |
||||||
229 | |||||||
230 | foreach ($chmod as $permission) |
||||||
231 | { |
||||||
232 | if (!$this->chmod($ftp_file, $permission)) |
||||||
233 | { |
||||||
234 | continue; |
||||||
235 | } |
||||||
236 | |||||||
237 | if (FileFunctions::instance()->isWritable($_SESSION['ftp_connection']['root'] . '/' . ltrim($ftp_file, '\/'))) |
||||||
238 | { |
||||||
239 | return true; |
||||||
240 | } |
||||||
241 | } |
||||||
242 | |||||||
243 | return false; |
||||||
244 | } |
||||||
245 | |||||||
246 | /** |
||||||
247 | * Deletes a file |
||||||
248 | * |
||||||
249 | * @param string $ftp_file The file to delete |
||||||
250 | * @return bool If delete was successful or not |
||||||
251 | */ |
||||||
252 | public function unlink($ftp_file) |
||||||
253 | { |
||||||
254 | // We are actually connected, right? |
||||||
255 | if (!$this->hasConnection()) |
||||||
256 | { |
||||||
257 | return false; |
||||||
258 | } |
||||||
259 | |||||||
260 | // Delete file X. |
||||||
261 | fwrite($this->connection, 'DELE ' . $ftp_file . "\r\n"); |
||||||
262 | if (!$this->check_response(250)) |
||||||
263 | { |
||||||
264 | fwrite($this->connection, 'RMD ' . $ftp_file . "\r\n"); |
||||||
265 | |||||||
266 | // Still no love? |
||||||
267 | if (!$this->check_response(250)) |
||||||
268 | { |
||||||
269 | $this->error = 'bad_file'; |
||||||
270 | |||||||
271 | return false; |
||||||
272 | } |
||||||
273 | } |
||||||
274 | |||||||
275 | return true; |
||||||
276 | } |
||||||
277 | |||||||
278 | /** |
||||||
279 | * Creates a new file on the server |
||||||
280 | * |
||||||
281 | * @param string $ftp_file The file to create |
||||||
282 | * @return bool If we were able to create the file |
||||||
283 | */ |
||||||
284 | public function create_file($ftp_file) |
||||||
285 | { |
||||||
286 | // First, we have to be connected... very important. |
||||||
287 | if (!$this->hasConnection()) |
||||||
288 | { |
||||||
289 | return false; |
||||||
290 | } |
||||||
291 | |||||||
292 | // I'd like one passive mode, please! |
||||||
293 | if (!$this->passive()) |
||||||
294 | { |
||||||
295 | return false; |
||||||
296 | } |
||||||
297 | |||||||
298 | // Seems logical enough, so far... |
||||||
299 | fwrite($this->connection, 'STOR ' . $ftp_file . "\r\n"); |
||||||
300 | |||||||
301 | // Okay, now we connect to the data port. If it doesn't work out, it's probably "file already exists", etc. |
||||||
302 | set_error_handler(static function () { /* ignore errors */ }); |
||||||
303 | $fp = stream_socket_client($this->pasv['ip'] . ':' . $this->pasv['port'], $err_code, $err, 5); |
||||||
304 | restore_error_handler(); |
||||||
305 | if ($fp === false || $err_code !== 0 || !$this->check_response(150)) |
||||||
306 | { |
||||||
307 | $this->error = 'bad_file'; |
||||||
308 | fclose($fp); |
||||||
309 | |||||||
310 | return false; |
||||||
311 | } |
||||||
312 | |||||||
313 | // This may look strange, but we're just closing it to indicate a zero-byte upload. |
||||||
314 | fclose($fp); |
||||||
315 | if (!$this->check_response(226)) |
||||||
316 | { |
||||||
317 | $this->error = 'bad_response'; |
||||||
318 | |||||||
319 | return false; |
||||||
320 | } |
||||||
321 | |||||||
322 | return true; |
||||||
323 | } |
||||||
324 | |||||||
325 | /** |
||||||
326 | * Used to create a passive connection |
||||||
327 | * |
||||||
328 | * @return bool If the connection was made or not |
||||||
329 | */ |
||||||
330 | public function passive() |
||||||
331 | { |
||||||
332 | // We can't create a passive data connection without a primary one first being there. |
||||||
333 | if (!$this->hasConnection()) |
||||||
334 | { |
||||||
335 | return false; |
||||||
336 | } |
||||||
337 | |||||||
338 | // Request a IPV4 passive connection - this means, we'll talk to you, you don't talk to us. |
||||||
339 | fwrite($this->connection, 'PASV' . "\r\n"); |
||||||
340 | |||||||
341 | // If it's not 227, we weren't given an IP and port, which means it failed. |
||||||
342 | // If it's 425, that may indicate a response to use EPSV (ipv6) which we don't support |
||||||
343 | if (!$this->check_response(227)) |
||||||
344 | { |
||||||
345 | $this->error = $this->last_response; |
||||||
346 | |||||||
347 | return false; |
||||||
348 | } |
||||||
349 | |||||||
350 | // Snatch the IP and port information, or die horribly trying... |
||||||
351 | if (preg_match('~\((\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+))\)~', $this->last_response, $match) !== 1) |
||||||
352 | { |
||||||
353 | $this->error = 'bad_response'; |
||||||
354 | |||||||
355 | return false; |
||||||
356 | } |
||||||
357 | |||||||
358 | // This is pretty simple - store it for later use ;). |
||||||
359 | $this->pasv = [ |
||||||
360 | 'ip' => $match[1] . '.' . $match[2] . '.' . $match[3] . '.' . $match[4], |
||||||
361 | 'port' => $match[5] * 256 + $match[6] |
||||||
362 | ]; |
||||||
363 | |||||||
364 | return true; |
||||||
365 | } |
||||||
366 | |||||||
367 | /** |
||||||
368 | * Creates a new directory on the server |
||||||
369 | * |
||||||
370 | * @param string $ftp_dir The name of the directory to create |
||||||
371 | * @return bool If the operation was successful |
||||||
372 | */ |
||||||
373 | public function create_dir($ftp_dir) |
||||||
374 | { |
||||||
375 | // We must be connected to the server to do something. |
||||||
376 | if (!$this->hasConnection()) |
||||||
377 | { |
||||||
378 | return false; |
||||||
379 | } |
||||||
380 | |||||||
381 | // Make this new beautiful directory! |
||||||
382 | fwrite($this->connection, 'MKD ' . $ftp_dir . "\r\n"); |
||||||
383 | if (!$this->check_response(257)) |
||||||
384 | { |
||||||
385 | $this->error = 'bad_file'; |
||||||
386 | |||||||
387 | return false; |
||||||
388 | } |
||||||
389 | |||||||
390 | return true; |
||||||
391 | } |
||||||
392 | |||||||
393 | /** |
||||||
394 | * Detects the current path |
||||||
395 | * |
||||||
396 | * @param string $filesystem_path The full path from the filesystem |
||||||
397 | * @param string|null $lookup_file The name of a file in the specified path |
||||||
398 | * @return array string $username, string $path, bool found_path |
||||||
399 | */ |
||||||
400 | public function detect_path($filesystem_path, $lookup_file = null) |
||||||
401 | { |
||||||
402 | $username = ''; |
||||||
403 | |||||||
404 | if (isset($_SERVER['DOCUMENT_ROOT'])) |
||||||
405 | { |
||||||
406 | if (preg_match('~^/home[2]?/([^/]+)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match)) |
||||||
407 | { |
||||||
408 | $username = $match[1]; |
||||||
409 | |||||||
410 | $path = strtr($_SERVER['DOCUMENT_ROOT'], array('/home/' . $match[1] . '/' => '', '/home2/' . $match[1] . '/' => '')); |
||||||
411 | |||||||
412 | if (substr($path, -1) === '/') |
||||||
413 | { |
||||||
414 | $path = substr($path, 0, -1); |
||||||
415 | } |
||||||
416 | |||||||
417 | if (strlen(dirname($_SERVER['PHP_SELF'])) > 1) |
||||||
418 | { |
||||||
419 | $path .= dirname($_SERVER['PHP_SELF']); |
||||||
420 | } |
||||||
421 | } |
||||||
422 | elseif (strpos($filesystem_path, '/var/www/') === 0) |
||||||
423 | { |
||||||
424 | $path = substr($filesystem_path, 8); |
||||||
425 | } |
||||||
426 | else |
||||||
427 | { |
||||||
428 | $path = strtr(strtr($filesystem_path, array('\\' => '/')), array($_SERVER['DOCUMENT_ROOT'] => '')); |
||||||
429 | } |
||||||
430 | } |
||||||
431 | else |
||||||
432 | { |
||||||
433 | $path = ''; |
||||||
434 | } |
||||||
435 | |||||||
436 | if ($this->hasConnection() && $this->list_dir($path) === '') |
||||||
0 ignored issues
–
show
|
|||||||
437 | { |
||||||
438 | $data = $this->list_dir('', true); |
||||||
439 | |||||||
440 | if ($lookup_file === null) |
||||||
441 | { |
||||||
442 | $lookup_file = $_SERVER['PHP_SELF']; |
||||||
443 | } |
||||||
444 | |||||||
445 | $found_path = dirname($this->locate('*' . basename(dirname($lookup_file)) . '/' . basename($lookup_file), $data)); |
||||||
446 | if ($found_path === '.') |
||||||
447 | { |
||||||
448 | $found_path = dirname($this->locate(basename($lookup_file))); |
||||||
449 | } |
||||||
450 | |||||||
451 | $path = $found_path; |
||||||
452 | } |
||||||
453 | elseif ($this->hasConnection()) |
||||||
454 | { |
||||||
455 | $found_path = true; |
||||||
456 | } |
||||||
457 | |||||||
458 | return [$username, $path, isset($found_path)]; |
||||||
459 | } |
||||||
460 | |||||||
461 | /** |
||||||
462 | * Generates a directory listing for the current directory |
||||||
463 | * |
||||||
464 | * @param string $ftp_path The path to the directory |
||||||
465 | * @param string|bool $search Whether or not to get a recursive directory listing |
||||||
466 | * @return false|string The results of the command or false if unsuccessful |
||||||
467 | */ |
||||||
468 | public function list_dir($ftp_path = '', $search = false) |
||||||
469 | { |
||||||
470 | // Are we even connected...? |
||||||
471 | if (!$this->hasConnection()) |
||||||
472 | { |
||||||
473 | return false; |
||||||
474 | } |
||||||
475 | |||||||
476 | // Passive... non-aggressive... |
||||||
477 | if (!$this->passive()) |
||||||
478 | { |
||||||
479 | return false; |
||||||
480 | } |
||||||
481 | |||||||
482 | // Get the listing! |
||||||
483 | fwrite($this->connection, 'LIST -1' . ($search ? 'R' : '') . ($ftp_path === '' ? '' : ' ' . $ftp_path) . "\r\n"); |
||||||
484 | |||||||
485 | // Connect, assuming we've got a connection. |
||||||
486 | $fp = @fsockopen($this->pasv['ip'], $this->pasv['port'], $err, $err, 5); |
||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||||
487 | if (!$fp || !$this->check_response(array(150, 125))) |
||||||
0 ignored issues
–
show
|
|||||||
488 | { |
||||||
489 | $this->error = 'bad_response'; |
||||||
490 | @fclose($fp); |
||||||
0 ignored issues
–
show
It seems like
$fp can also be of type false ; however, parameter $stream of fclose() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() It seems like you do not handle an error condition for
fclose() . This can introduce security issues, and is generally not recommended.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
![]() |
|||||||
491 | |||||||
492 | return false; |
||||||
493 | } |
||||||
494 | |||||||
495 | // Read in the file listing. |
||||||
496 | $data = ''; |
||||||
497 | while (!feof($fp)) |
||||||
498 | { |
||||||
499 | $data .= fread($fp, 4096); |
||||||
500 | } |
||||||
501 | |||||||
502 | fclose($fp); |
||||||
503 | |||||||
504 | // Everything go okay? |
||||||
505 | if (!$this->check_response(226)) |
||||||
506 | { |
||||||
507 | $this->error = 'bad_response'; |
||||||
508 | |||||||
509 | return false; |
||||||
510 | } |
||||||
511 | |||||||
512 | return $data; |
||||||
513 | } |
||||||
514 | |||||||
515 | /** |
||||||
516 | * Determines the current directory we are in |
||||||
517 | * |
||||||
518 | * @param string $file The name of a file |
||||||
519 | * @param string|null $listing A directory listing or null to generate one |
||||||
520 | * @return string|false The name of the file or false if it wasn't found |
||||||
521 | */ |
||||||
522 | public function locate($file, $listing = null) |
||||||
523 | { |
||||||
524 | if ($listing === null) |
||||||
525 | { |
||||||
526 | $listing = $this->list_dir('', true); |
||||||
527 | } |
||||||
528 | |||||||
529 | $listing = explode("\n", $listing); |
||||||
0 ignored issues
–
show
It seems like
$listing can also be of type false ; however, parameter $string of explode() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
530 | |||||||
531 | $current_dir = ''; |
||||||
532 | fwrite($this->connection, 'PWD' . "\r\n"); |
||||||
0 ignored issues
–
show
It seems like
$this->connection can also be of type string ; however, parameter $stream of fwrite() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
533 | if ($this->check_response(257)) |
||||||
534 | { |
||||||
535 | $current_dir = strtr($this->last_response, ['""' => '"']); |
||||||
536 | } |
||||||
537 | |||||||
538 | for ($i = 0, $n = count($listing); $i < $n; $i++) |
||||||
539 | { |
||||||
540 | if (trim($listing[$i]) === '' && isset($listing[$i + 1])) |
||||||
541 | { |
||||||
542 | $current_dir = substr(trim($listing[++$i]), 0, -1); |
||||||
543 | $i++; |
||||||
544 | } |
||||||
545 | |||||||
546 | // Okay, this file's name is: |
||||||
547 | $listing[$i] = $current_dir . '/' . trim(strlen($listing[$i]) > 30 ? strrchr($listing[$i], ' ') : $listing[$i]); |
||||||
548 | |||||||
549 | if ($file[0] === '*' && substr($listing[$i], -(strlen($file) - 1)) === substr($file, 1)) |
||||||
550 | { |
||||||
551 | return $listing[$i]; |
||||||
552 | } |
||||||
553 | |||||||
554 | if (substr($file, -1) === '*' && substr($listing[$i], 0, strlen($file) - 1) === substr($file, 0, -1)) |
||||||
555 | { |
||||||
556 | return $listing[$i]; |
||||||
557 | } |
||||||
558 | |||||||
559 | if (basename($listing[$i]) === $file || $listing[$i] === $file) |
||||||
560 | { |
||||||
561 | return $listing[$i]; |
||||||
562 | } |
||||||
563 | } |
||||||
564 | |||||||
565 | return false; |
||||||
566 | } |
||||||
567 | |||||||
568 | /** |
||||||
569 | * Close the ftp connection |
||||||
570 | * |
||||||
571 | * @return bool |
||||||
572 | */ |
||||||
573 | public function close() |
||||||
574 | { |
||||||
575 | // Goodbye! |
||||||
576 | if ($this->hasConnection()) |
||||||
577 | { |
||||||
578 | fwrite($this->connection, 'QUIT' . "\r\n"); |
||||||
579 | fclose($this->connection); |
||||||
580 | } |
||||||
581 | |||||||
582 | return true; |
||||||
583 | } |
||||||
584 | |||||||
585 | /** |
||||||
586 | * If we are connected |
||||||
587 | * |
||||||
588 | * @return bool |
||||||
589 | */ |
||||||
590 | public function hasConnection() |
||||||
591 | { |
||||||
592 | return is_resource($this->connection); |
||||||
593 | } |
||||||
594 | } |
||||||
595 |