1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage FileSystem
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 /**
13 * File system helper
14 *
15 * Holds support functions for the filesystem, particularly the stream
16 *
17 * @since 11.1
18 */
19 class JFilesystemHelper
20 {
21 /**
22 * Remote file size function for streams that don't support it
23 *
24 * @param string $url TODO Add text
25 *
26 * @return mixed
27 *
28 * @link https://secure.php.net/manual/en/function.filesize.php#71098
29 * @since 11.1
30 */
31 public static function remotefsize($url)
32 {
33 $sch = parse_url($url, PHP_URL_SCHEME);
34
35 if (($sch != 'http') && ($sch != 'https') && ($sch != 'ftp') && ($sch != 'ftps'))
36 {
37 return false;
38 }
39
40 if (($sch == 'http') || ($sch == 'https'))
41 {
42 $headers = get_headers($url, 1);
43
44 if ((!array_key_exists('Content-Length', $headers)))
45 {
46 return false;
47 }
48
49 return $headers['Content-Length'];
50 }
51
52 if (($sch == 'ftp') || ($sch == 'ftps'))
53 {
54 $server = parse_url($url, PHP_URL_HOST);
55 $port = parse_url($url, PHP_URL_PORT);
56 $path = parse_url($url, PHP_URL_PATH);
57 $user = parse_url($url, PHP_URL_USER);
58 $pass = parse_url($url, PHP_URL_PASS);
59
60 if ((!$server) || (!$path))
61 {
62 return false;
63 }
64
65 if (!$port)
66 {
67 $port = 21;
68 }
69
70 if (!$user)
71 {
72 $user = 'anonymous';
73 }
74
75 if (!$pass)
76 {
77 $pass = '';
78 }
79
80 switch ($sch)
81 {
82 case 'ftp':
83 $ftpid = ftp_connect($server, $port);
84 break;
85
86 case 'ftps':
87 $ftpid = ftp_ssl_connect($server, $port);
88 break;
89 }
90
91 if (!$ftpid)
92 {
93 return false;
94 }
95
96 $login = ftp_login($ftpid, $user, $pass);
97
98 if (!$login)
99 {
100 return false;
101 }
102
103 $ftpsize = ftp_size($ftpid, $path);
104 ftp_close($ftpid);
105
106 if ($ftpsize == -1)
107 {
108 return false;
109 }
110
111 return $ftpsize;
112 }
113 }
114
115 /**
116 * Quick FTP chmod
117 *
118 * @param string $url Link identifier
119 * @param integer $mode The new permissions, given as an octal value.
120 *
121 * @return mixed
122 *
123 * @link https://secure.php.net/manual/en/function.ftp-chmod.php
124 * @since 11.1
125 */
126 public static function ftpChmod($url, $mode)
127 {
128 $sch = parse_url($url, PHP_URL_SCHEME);
129
130 if (($sch != 'ftp') && ($sch != 'ftps'))
131 {
132 return false;
133 }
134
135 $server = parse_url($url, PHP_URL_HOST);
136 $port = parse_url($url, PHP_URL_PORT);
137 $path = parse_url($url, PHP_URL_PATH);
138 $user = parse_url($url, PHP_URL_USER);
139 $pass = parse_url($url, PHP_URL_PASS);
140
141 if ((!$server) || (!$path))
142 {
143 return false;
144 }
145
146 if (!$port)
147 {
148 $port = 21;
149 }
150
151 if (!$user)
152 {
153 $user = 'anonymous';
154 }
155
156 if (!$pass)
157 {
158 $pass = '';
159 }
160
161 switch ($sch)
162 {
163 case 'ftp':
164 $ftpid = ftp_connect($server, $port);
165 break;
166
167 case 'ftps':
168 $ftpid = ftp_ssl_connect($server, $port);
169 break;
170 }
171
172 if (!$ftpid)
173 {
174 return false;
175 }
176
177 $login = ftp_login($ftpid, $user, $pass);
178
179 if (!$login)
180 {
181 return false;
182 }
183
184 $res = ftp_chmod($ftpid, $mode, $path);
185 ftp_close($ftpid);
186
187 return $res;
188 }
189
190 /**
191 * Modes that require a write operation
192 *
193 * @return array
194 *
195 * @since 11.1
196 */
197 public static function getWriteModes()
198 {
199 return array('w', 'w+', 'a', 'a+', 'r+', 'x', 'x+');
200 }
201
202 /**
203 * Stream and Filter Support Operations
204 *
205 * Returns the supported streams, in addition to direct file access
206 * Also includes Joomla! streams as well as PHP streams
207 *
208 * @return array Streams
209 *
210 * @since 11.1
211 */
212 public static function getSupported()
213 {
214 // Really quite cool what php can do with arrays when you let it...
215 static $streams;
216
217 if (!$streams)
218 {
219 $streams = array_merge(stream_get_wrappers(), self::getJStreams());
220 }
221
222 return $streams;
223 }
224
225 /**
226 * Returns a list of transports
227 *
228 * @return array
229 *
230 * @since 11.1
231 */
232 public static function getTransports()
233 {
234 // Is this overkill?
235 return stream_get_transports();
236 }
237
238 /**
239 * Returns a list of filters
240 *
241 * @return array
242 *
243 * @since 11.1
244 */
245 public static function getFilters()
246 {
247 // Note: This will look like the getSupported() function with J! filters.
248 // TODO: add user space filter loading like user space stream loading
249 return stream_get_filters();
250 }
251
252 /**
253 * Returns a list of J! streams
254 *
255 * @return array
256 *
257 * @since 11.1
258 */
259 public static function getJStreams()
260 {
261 static $streams = array();
262
263 if (!$streams)
264 {
265 $files = new DirectoryIterator(__DIR__ . '/streams');
266
267 /* @type $file DirectoryIterator */
268 foreach ($files as $file)
269 {
270 // Only load for php files.
271 if (!$file->isFile() || $file->getExtension() !== 'php')
272 {
273 continue;
274 }
275
276 $streams[] = $file->getBasename('.php');
277 }
278 }
279
280 return $streams;
281 }
282
283 /**
284 * Determine if a stream is a Joomla stream.
285 *
286 * @param string $streamname The name of a stream
287 *
288 * @return boolean True for a Joomla Stream
289 *
290 * @since 11.1
291 */
292 public static function isJoomlaStream($streamname)
293 {
294 return in_array($streamname, self::getJStreams());
295 }
296
297 /**
298 * Calculates the maximum upload file size and returns string with unit or the size in bytes
299 *
300 * Call it with JFilesystemHelper::fileUploadMaxSize();
301 *
302 * @param bool $unit_output This parameter determines whether the return value should be a string with a unit
303 *
304 * @return float|string The maximum upload size of files with the appropriate unit or in bytes
305 *
306 * @since 3.4
307 */
308 public static function fileUploadMaxSize($unit_output = true)
309 {
310 static $max_size = false;
311 static $output_type = true;
312
313 if ($max_size === false || $output_type != $unit_output)
314 {
315 $max_size = self::parseSize(ini_get('post_max_size'));
316 $upload_max = self::parseSize(ini_get('upload_max_filesize'));
317
318 if ($upload_max > 0 && ($upload_max < $max_size || $max_size == 0))
319 {
320 $max_size = $upload_max;
321 }
322
323 if ($unit_output == true)
324 {
325 $max_size = self::parseSizeUnit($max_size);
326 }
327
328 $output_type = $unit_output;
329 }
330
331 return $max_size;
332 }
333
334 /**
335 * Returns the size in bytes without the unit for the comparison
336 *
337 * @param string $size The size which is received from the PHP settings
338 *
339 * @return float The size in bytes without the unit
340 *
341 * @since 3.4
342 */
343 private static function parseSize($size)
344 {
345 $unit = preg_replace('/[^bkmgtpezy]/i', '', $size);
346 $size = preg_replace('/[^0-9\.]/', '', $size);
347
348 $return = round($size);
349
350 if ($unit)
351 {
352 $return = round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
353 }
354
355 return $return;
356 }
357
358 /**
359 * Creates the rounded size of the size with the appropriate unit
360 *
361 * @param float $max_size The maximum size which is allowed for the uploads
362 *
363 * @return string String with the size and the appropriate unit
364 *
365 * @since 3.4
366 */
367 private static function parseSizeUnit($max_size)
368 {
369 $base = log($max_size) / log(1024);
370 $suffixes = array('', 'k', 'M', 'G', 'T');
371
372 return round(pow(1024, $base - floor($base)), 0) . $suffixes[floor($base)];
373 }
374 }
375