elkarte /
Elkarte
| 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
Loading history...
|
|||||||
| 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): string |
||||||
| 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'], ['/' => '', ':' => '', '@' => '']); |
||||||
| 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): bool |
||||||
| 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
Loading history...
|
|||||||
| 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): bool |
||||||
| 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): bool |
||||||
| 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): bool |
||||||
| 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): bool |
||||||
| 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): bool |
||||||
| 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(): bool |
||||||
| 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): bool |
||||||
| 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): array |
||||||
| 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'], ['/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, ['\\' => '/']), [$_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([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
Loading history...
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.');
}
Loading history...
|
|||||||
| 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
Loading history...
|
|||||||
| 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
Loading history...
|
|||||||
| 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(): bool |
||||||
| 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(): bool |
||||||
| 591 | { |
||||||
| 592 | return is_resource($this->connection); |
||||||
| 593 | } |
||||||
| 594 | } |
||||||
| 595 |