Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
35 / 35 |
|
100.00% |
4 / 4 |
CRAP | |
100.00% |
1 / 1 |
file_downloader | |
100.00% |
35 / 35 |
|
100.00% |
4 / 4 |
12 | |
100.00% |
1 / 1 |
create_client | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
3 | |||
get | |
100.00% |
22 / 22 |
|
100.00% |
1 / 1 |
7 | |||
get_error_string | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
get_error_number | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * |
4 | * This file is part of the phpBB Forum Software package. |
5 | * |
6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
7 | * @license GNU General Public License, version 2 (GPL-2.0) |
8 | * |
9 | * For full copyright and license information, please see |
10 | * the docs/CREDITS.txt file. |
11 | * |
12 | */ |
13 | |
14 | namespace phpbb; |
15 | |
16 | use GuzzleHttp\Client; |
17 | use GuzzleHttp\Exception\RequestException; |
18 | use phpbb\exception\runtime_exception; |
19 | |
20 | class file_downloader |
21 | { |
22 | const OK = 200; |
23 | const NOT_FOUND = 404; |
24 | const REQUEST_TIMEOUT = 408; |
25 | |
26 | /** @var string Error string */ |
27 | protected string $error_string = ''; |
28 | |
29 | /** @var int Error number */ |
30 | protected int $error_number = 0; |
31 | |
32 | /** |
33 | * Create new guzzle client |
34 | * |
35 | * @param string $host |
36 | * @param int $port |
37 | * @param int $timeout |
38 | * |
39 | * @return Client |
40 | */ |
41 | protected function create_client(string $host, int $port = 443, int $timeout = 6): Client |
42 | { |
43 | // Only set URL scheme if not specified in URL |
44 | $url_parts = parse_url($host); |
45 | if (!isset($url_parts['scheme'])) |
46 | { |
47 | $host = (($port === 443) ? 'https://' : 'http://') . $host; |
48 | } |
49 | |
50 | // Initialize Guzzle client |
51 | return new Client([ |
52 | 'base_uri' => $host, |
53 | 'timeout' => $timeout, |
54 | 'headers' => [ |
55 | 'user-agent' => 'phpBB/' . PHPBB_VERSION, |
56 | 'accept' => '*/*', |
57 | ], |
58 | ]); |
59 | } |
60 | |
61 | /** |
62 | * Retrieve contents from remotely stored file |
63 | * |
64 | * @param string $host File host |
65 | * @param string $directory Directory file is in |
66 | * @param string $filename Filename of file to retrieve |
67 | * @param int $port Port to connect to; default: 443 |
68 | * @param int $timeout Connection timeout in seconds; default: 6 |
69 | * |
70 | * @return false|string File data as string if file can be read and there is no |
71 | * timeout, false if there were errors or the connection timed out |
72 | * |
73 | * @throws runtime_exception If data can't be retrieved and no error |
74 | * message is returned |
75 | */ |
76 | public function get(string $host, string $directory, string $filename, int $port = 443, int $timeout = 6): bool|string |
77 | { |
78 | // Initialize Guzzle client |
79 | $client = $this->create_client($host, $port, $timeout); |
80 | |
81 | // Set default values for error variables |
82 | $this->error_number = 0; |
83 | $this->error_string = ''; |
84 | |
85 | try |
86 | { |
87 | $response = $client->request('GET', "$directory/$filename"); |
88 | |
89 | // Check if the response status code is 200 (OK) |
90 | if ($response->getStatusCode() == self::OK) |
91 | { |
92 | return $response->getBody()->getContents(); |
93 | } |
94 | else |
95 | { |
96 | $this->error_number = $response->getStatusCode(); |
97 | throw new runtime_exception('FILE_NOT_FOUND', [$filename]); |
98 | } |
99 | } |
100 | catch (RequestException $exception) |
101 | { |
102 | if ($exception->hasResponse()) |
103 | { |
104 | $this->error_number = $exception->getResponse()->getStatusCode(); |
105 | |
106 | if ($this->error_number == self::NOT_FOUND) |
107 | { |
108 | throw new runtime_exception('FILE_NOT_FOUND', [$filename]); |
109 | } |
110 | } |
111 | else |
112 | { |
113 | $this->error_number = self::REQUEST_TIMEOUT; |
114 | throw new runtime_exception('FSOCK_TIMEOUT'); |
115 | } |
116 | |
117 | $this->error_string = utf8_convert_message($exception->getMessage()); |
118 | return false; |
119 | } |
120 | catch (runtime_exception $exception) |
121 | { |
122 | // Rethrow runtime_exceptions, only handle unknown cases below |
123 | throw $exception; |
124 | } |
125 | catch (\Throwable $exception) |
126 | { |
127 | $this->error_string = utf8_convert_message($exception->getMessage()); |
128 | throw new runtime_exception('FSOCK_DISABLED'); |
129 | } |
130 | } |
131 | |
132 | /** |
133 | * Get error string |
134 | * |
135 | * @return string Error string |
136 | */ |
137 | public function get_error_string(): string |
138 | { |
139 | return $this->error_string; |
140 | } |
141 | |
142 | /** |
143 | * Get error number |
144 | * |
145 | * @return int Error number |
146 | */ |
147 | public function get_error_number(): int |
148 | { |
149 | return $this->error_number; |
150 | } |
151 | } |