Fixed bug #15034: Login to backend fails with IPv6 Address as HTTP_HOST (Thanks to...
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_div.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2010 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Contains the reknown class "t3lib_div" with general purpose functions
29 *
30 * $Id$
31 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj
32 * XHTML compliant
33 * Usage counts are based on search 22/2 2003 through whole source including tslib/
34 *
35 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
36 */
37 /**
38 * [CLASS/FUNCTION INDEX of SCRIPT]
39 *
40 *
41 *
42 * 232: class t3lib_div
43 *
44 * SECTION: GET/POST Variables
45 * 262: function _GP($var)
46 * 280: function _GET($var=NULL)
47 * 297: function _POST($var=NULL)
48 * 313: function _GETset($inputGet,$key='')
49 * 336: function GPvar($var,$strip=0)
50 * 353: function GParrayMerged($var)
51 *
52 * SECTION: IMAGE FUNCTIONS
53 * 397: function gif_compress($theFile, $type)
54 * 425: function png_to_gif_by_imagemagick($theFile)
55 * 450: function read_png_gif($theFile,$output_png=0)
56 *
57 * SECTION: STRING FUNCTIONS
58 * 499: function fixed_lgd($string,$origChars,$preStr='...')
59 * 524: function fixed_lgd_pre($string,$chars)
60 * 538: function fixed_lgd_cs($string,$chars)
61 * 555: function breakTextForEmail($str,$implChar=LF,$charWidth=76)
62 * 574: function breakLinesForEmail($str,$implChar=LF,$charWidth=76)
63 * 610: function cmpIP($baseIP, $list)
64 * 626: function cmpIPv4($baseIP, $list)
65 * 668: function cmpIPv6($baseIP, $list)
66 * 711: function IPv6Hex2Bin ($hex)
67 * 726: function normalizeIPv6($address)
68 * 782: function validIPv6($ip)
69 * 805: function cmpFQDN($baseIP, $list)
70 * 835: function inList($list,$item)
71 * 847: function rmFromList($element,$list)
72 * 863: function expandList($list)
73 * 894: function intInRange($theInt,$min,$max=2000000000,$zeroValue=0)
74 * 910: function intval_positive($theInt)
75 * 923: function int_from_ver($verNumberStr)
76 * 934: function compat_version($verNumberStr)
77 * 952: function md5int($str)
78 * 965: function shortMD5($input, $len=10)
79 * 978: function uniqueList($in_list, $secondParameter=NULL)
80 * 992: function split_fileref($fileref)
81 * 1030: function dirname($path)
82 * 1046: function modifyHTMLColor($color,$R,$G,$B)
83 * 1066: function modifyHTMLColorAll($color,$all)
84 * 1077: function rm_endcomma($string)
85 * 1090: function danish_strtoupper($string)
86 * 1105: function convUmlauts($str)
87 * 1118: function testInt($var)
88 * 1130: function isFirstPartOfStr($str,$partStr)
89 * 1146: function formatSize($sizeInBytes,$labels='')
90 * 1181: function convertMicrotime($microtime)
91 * 1195: function splitCalc($string,$operators)
92 * 1217: function calcPriority($string)
93 * 1258: function calcParenthesis($string)
94 * 1284: function htmlspecialchars_decode($value)
95 * 1299: function deHSCentities($str)
96 * 1312: function slashJS($string,$extended=0,$char="'")
97 * 1325: function rawUrlEncodeJS($str)
98 * 1337: function rawUrlEncodeFP($str)
99 * 1348: function validEmail($email)
100 * 1363: function formatForTextarea($content)
101 *
102 * SECTION: ARRAY FUNCTIONS
103 * 1394: function inArray($in_array,$item)
104 * 1411: function intExplode($delim, $string)
105 * 1430: function revExplode($delim, $string, $count=0)
106 * 1450: function trimExplode($delim, $string, $onlyNonEmptyValues=0)
107 * 1472: function uniqueArray($valueArray)
108 * 1484: function removeArrayEntryByValue($array,$cmpValue)
109 * 1513: function implodeArrayForUrl($name,$theArray,$str='',$skipBlank=0,$rawurlencodeParamName=0)
110 * 1538: function explodeUrl2Array($string,$multidim=FALSE)
111 * 1564: function compileSelectedGetVarsFromArray($varList,$getArray,$GPvarAlt=1)
112 * 1587: function addSlashesOnArray(&$theArray)
113 * 1611: function stripSlashesOnArray(&$theArray)
114 * 1633: function slashArray($arr,$cmd)
115 * 1650: function array_merge_recursive_overrule($arr0,$arr1,$notAddKeys=0,$includeEmtpyValues=true)
116 * 1683: function array_merge($arr1,$arr2)
117 * 1696: function csvValues($row,$delim=',',$quote='"')
118 *
119 * SECTION: HTML/XML PROCESSING
120 * 1738: function get_tag_attributes($tag)
121 * 1775: function split_tag_attributes($tag)
122 * 1809: function implodeAttributes($arr,$xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE)
123 * 1836: function implodeParams($arr,$xhtmlSafe=FALSE,$dontOmitBlankAttribs=FALSE)
124 * 1851: function wrapJS($string, $linebreak=TRUE)
125 * 1882: function xml2tree($string,$depth=999)
126 * 1969: function array2xml($array,$NSprefix='',$level=0,$docTag='phparray',$spaceInd=0, $options=array(),$stackData=array())
127 * 2088: function xml2array($string,$NSprefix='',$reportDocTag=FALSE)
128 * 2198: function xmlRecompileFromStructValArray($vals)
129 * 2242: function xmlGetHeaderAttribs($xmlData)
130 *
131 * SECTION: FILES FUNCTIONS
132 * 2275: function getURL($url, $includeHeader=0)
133 * 2342: function writeFile($file,$content)
134 * 2367: function fixPermissions($file)
135 * 2384: function writeFileToTypo3tempDir($filepath,$content)
136 * 2427: function mkdir($theNewFolder)
137 * 2446: function mkdir_deep($destination,$deepDir)
138 * 2468: function get_dirs($path)
139 * 2493: function getFilesInDir($path,$extensionList='',$prependPath=0,$order='')
140 * 2547: function getAllFilesAndFoldersInPath($fileArr,$path,$extList='',$regDirs=0,$recursivityLevels=99)
141 * 2570: function removePrefixPathFromList($fileArr,$prefixToRemove)
142 * 2586: function fixWindowsFilePath($theFile)
143 * 2598: function resolveBackPath($pathStr)
144 * 2626: function locationHeaderUrl($path)
145 *
146 * SECTION: DEBUG helper FUNCTIONS
147 * 2666: function debug_ordvalue($string,$characters=100)
148 * 2683: function view_array($array_in)
149 * 2711: function print_array($array_in)
150 * 2726: function debug($var="",$brOrHeader=0)
151 * 2757: function debug_trail()
152 * 2779: function debugRows($rows,$header='')
153 *
154 * SECTION: SYSTEM INFORMATION
155 * 2857: function getThisUrl()
156 * 2873: function linkThisScript($getParams=array())
157 * 2897: function linkThisUrl($url,$getParams=array())
158 * 2920: function getIndpEnv($getEnvName)
159 * 3113: function milliseconds()
160 * 3125: function clientInfo($useragent='')
161 *
162 * SECTION: TYPO3 SPECIFIC FUNCTIONS
163 * 3212: function getFileAbsFileName($filename,$onlyRelative=1,$relToTYPO3_mainDir=0)
164 * 3248: function validPathStr($theFile)
165 * 3259: function isAbsPath($path)
166 * 3270: function isAllowedAbsPath($path)
167 * 3287: function verifyFilenameAgainstDenyPattern($filename)
168 * 3305: function upload_copy_move($source,$destination)
169 * 3331: function upload_to_tempfile($uploadedFileName)
170 * 3349: function unlink_tempfile($uploadedTempFileName)
171 * 3365: function tempnam($filePrefix)
172 * 3379: function stdAuthCode($uid_or_record,$fields='',$codeLength=8)
173 * 3410: function cHashParams($addQueryParams)
174 * 3433: function hideIfNotTranslated($l18n_cfg_fieldValue)
175 * 3448: function readLLfile($fileRef,$langKey)
176 * 3472: function readLLXMLfile($fileRef,$langKey)
177 * 3589: function llXmlAutoFileName($fileRef,$language)
178 * 3633: function loadTCA($table)
179 * 3653: function resolveSheetDefInDS($dataStructArray,$sheet='sDEF')
180 * 3686: function resolveAllSheetsInDS($dataStructArray)
181 * 3715: function callUserFunction($funcName,&$params,&$ref,$checkPrefix='user_',$silent=0)
182 * 3813: function &getUserObj($classRef,$checkPrefix='user_',$silent=0)
183 * 3871: function &makeInstance($className)
184 * 3883: function makeInstanceClassName($className)
185 * 3897: function &makeInstanceService($serviceType, $serviceSubType='', $excludeServiceKeys=array())
186 * 3961: function plainMailEncoded($email,$subject,$message,$headers='',$enc='',$charset='',$dontEncodeHeader=false)
187 * 4031: function quoted_printable($string,$maxlen=76)
188 * 4078: function encodeHeader($line,$enc='',$charset='ISO-8859-1')
189 * 4121: function substUrlsInPlainText($message,$urlmode='76',$index_script_url='')
190 * 4155: function makeRedirectUrl($inUrl,$l=0,$index_script_url='')
191 * 4182: function freetypeDpiComp($font_size)
192 * 4194: function initSysLog()
193 * 4251: function sysLog($msg, $extKey, $severity=0)
194 * 4334: function devLog($msg, $extKey, $severity=0, $dataVar=FALSE)
195 * 4355: function arrayToLogString($arr, $valueList=array(), $valueLength=20)
196 * 4378: function imageMagickCommand($command, $parameters, $path='')
197 * 4425: function unQuoteFilenames($parameters,$unQuote=FALSE)
198 * 4459: function quoteJSvalue($value, $inScriptTags = false)
199 *
200 * TOTAL FUNCTIONS: 138
201 * (This index is automatically created/updated by the extension "extdeveval")
202 *
203 */
204
205 // a tabulator
206 define('TAB', chr(9));
207 // a linefeed
208 define('LF', chr(10));
209 // a carriage return
210 define('CR', chr(13));
211 // a CR-LF combination
212 define('CRLF', CR . LF);
213
214 /**
215 * The legendary "t3lib_div" class - Miscellaneous functions for general purpose.
216 * Most of the functions does not relate specifically to TYPO3
217 * However a section of functions requires certain TYPO3 features available
218 * See comments in the source.
219 * You are encouraged to use this library in your own scripts!
220 *
221 * USE:
222 * The class is intended to be used without creating an instance of it.
223 * So: Don't instantiate - call functions with "t3lib_div::" prefixed the function name.
224 * So use t3lib_div::[method-name] to refer to the functions, eg. 't3lib_div::milliseconds()'
225 *
226 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
227 * @package TYPO3
228 * @subpackage t3lib
229 */
230 final class t3lib_div {
231
232 // Severity constants used by t3lib_div::sysLog()
233 const SYSLOG_SEVERITY_INFO = 0;
234 const SYSLOG_SEVERITY_NOTICE = 1;
235 const SYSLOG_SEVERITY_WARNING = 2;
236 const SYSLOG_SEVERITY_ERROR = 3;
237 const SYSLOG_SEVERITY_FATAL = 4;
238
239 /**
240 * Singleton instances returned by makeInstance, using the class names as
241 * array keys
242 *
243 * @var array<t3lib_Singleton>
244 */
245 protected static $singletonInstances = array();
246
247 /**
248 * Instances returned by makeInstance, using the class names as array keys
249 *
250 * @var array<array><object>
251 */
252 protected static $nonSingletonInstances = array();
253
254 /*************************
255 *
256 * GET/POST Variables
257 *
258 * Background:
259 * Input GET/POST variables in PHP may have their quotes escaped with "\" or not depending on configuration.
260 * TYPO3 has always converted quotes to BE escaped if the configuration told that they would not be so.
261 * But the clean solution is that quotes are never escaped and that is what the functions below offers.
262 * Eventually TYPO3 should provide this in the global space as well.
263 * In the transitional phase (or forever..?) we need to encourage EVERY to read and write GET/POST vars through the API functions below.
264 *
265 *************************/
266
267 /**
268 * Returns the 'GLOBAL' value of incoming data from POST or GET, with priority to POST (that is equalent to 'GP' order)
269 * Strips slashes from all output, both strings and arrays.
270 * This function substitutes t3lib_div::GPvar()
271 * To enhancement security in your scripts, please consider using t3lib_div::_GET or t3lib_div::_POST if you already know by which method your data is arriving to the scripts!
272 * Usage: 537
273 *
274 * @param string GET/POST var to return
275 * @return mixed POST var named $var and if not set, the GET var of the same name.
276 */
277 public static function _GP($var) {
278 if (empty($var)) {
279 return;
280 }
281 $value = isset($_POST[$var]) ? $_POST[$var] : $_GET[$var];
282 if (isset($value)) {
283 if (is_array($value)) {
284 self::stripSlashesOnArray($value);
285 } else {
286 $value = stripslashes($value);
287 }
288 }
289 return $value;
290 }
291
292 /**
293 * Returns the global arrays $_GET and $_POST merged with $_POST taking precedence.
294 *
295 * @param string Key (variable name) from GET or POST vars
296 * @return array Returns the GET vars merged recursively onto the POST vars.
297 */
298 public static function _GPmerged($parameter) {
299 $postParameter = (isset($_POST[$parameter]) && is_array($_POST[$parameter])) ? $_POST[$parameter] : array();
300 $getParameter = (isset($_GET[$parameter]) && is_array($_GET[$parameter])) ? $_GET[$parameter] : array();
301
302 $mergedParameters = self::array_merge_recursive_overrule($getParameter, $postParameter);
303 self::stripSlashesOnArray($mergedParameters);
304
305 return $mergedParameters;
306 }
307
308 /**
309 * Returns the global $_GET array (or value from) normalized to contain un-escaped values.
310 * ALWAYS use this API function to acquire the GET variables!
311 * Usage: 27
312 *
313 * @param string Optional pointer to value in GET array (basically name of GET var)
314 * @return mixed If $var is set it returns the value of $_GET[$var]. If $var is NULL (default), returns $_GET itself. In any case *slashes are stipped from the output!*
315 * @see _POST(), _GP(), _GETset()
316 */
317 public static function _GET($var = NULL) {
318 $value = ($var === NULL) ? $_GET : (empty($var) ? NULL : $_GET[$var]);
319 if (isset($value)) { // Removes slashes since TYPO3 has added them regardless of magic_quotes setting.
320 if (is_array($value)) {
321 self::stripSlashesOnArray($value);
322 } else {
323 $value = stripslashes($value);
324 }
325 }
326 return $value;
327 }
328
329 /**
330 * Returns the global $_POST array (or value from) normalized to contain un-escaped values.
331 * ALWAYS use this API function to acquire the $_POST variables!
332 * Usage: 41
333 *
334 * @param string Optional pointer to value in POST array (basically name of POST var)
335 * @return mixed If $var is set it returns the value of $_POST[$var]. If $var is NULL (default), returns $_POST itself. In any case *slashes are stipped from the output!*
336 * @see _GET(), _GP()
337 */
338 public static function _POST($var = NULL) {
339 $value = ($var === NULL) ? $_POST : (empty($var) ? NULL : $_POST[$var]);
340 if (isset($value)) { // Removes slashes since TYPO3 has added them regardless of magic_quotes setting.
341 if (is_array($value)) {
342 self::stripSlashesOnArray($value);
343 } else {
344 $value = stripslashes($value);
345 }
346 }
347 return $value;
348 }
349
350 /**
351 * Writes input value to $_GET.
352 * Usage: 2
353 *
354 * @param mixed $inputGet
355 * array or single value to write to $_GET. Values should NOT be
356 * escaped at input time (but will be escaped before writing
357 * according to TYPO3 standards).
358 * @param string $key
359 * alternative key; If set, this will not set the WHOLE GET array,
360 * but only the key in it specified by this value!
361 * You can specify to replace keys on deeper array levels by
362 * separating the keys with a pipe.
363 * Example: 'parentKey|childKey' will result in
364 * array('parentKey' => array('childKey' => $inputGet))
365 *
366 * @return void
367 */
368 public static function _GETset($inputGet, $key = '') {
369 // adds slashes since TYPO3 standard currently is that slashes
370 // must be applied (regardless of magic_quotes setting)
371 if (is_array($inputGet)) {
372 self::addSlashesOnArray($inputGet);
373 } else {
374 $inputGet = addslashes($inputGet);
375 }
376
377 if ($key != '') {
378 if (strpos($key, '|') !== FALSE) {
379 $pieces = explode('|', $key);
380 $newGet = array();
381 $pointer =& $newGet;
382 foreach ($pieces as $piece) {
383 $pointer =& $pointer[$piece];
384 }
385 $pointer = $inputGet;
386 $mergedGet = self::array_merge_recursive_overrule(
387 $_GET, $newGet
388 );
389
390 $_GET = $mergedGet;
391 $GLOBALS['HTTP_GET_VARS'] = $mergedGet;
392 } else {
393 $_GET[$key] = $inputGet;
394 $GLOBALS['HTTP_GET_VARS'][$key] = $inputGet;
395 }
396 } elseif (is_array($inputGet)) {
397 $_GET = $inputGet;
398 $GLOBALS['HTTP_GET_VARS'] = $inputGet;
399 }
400 }
401
402 /**
403 * Returns the value of incoming data from globals variable $_POST or $_GET, with priority to $_POST (that is equalent to 'GP' order).
404 * Strips slashes of string-outputs, but not arrays UNLESS $strip is set. If $strip is set all output will have escaped characters unescaped.
405 * Usage: 2
406 *
407 * @param string GET/POST var to return
408 * @param boolean If set, values are stripped of return values that are *arrays!* - string/integer values returned are always strip-slashed()
409 * @return mixed POST var named $var and if not set, the GET var of the same name.
410 * @deprecated since TYPO3 3.6, will be removed in TYPO3 4.6 - Use t3lib_div::_GP instead (ALWAYS delivers a value with un-escaped values!)
411 * @see _GP()
412 */
413 public static function GPvar($var, $strip = 0) {
414 self::logDeprecatedFunction();
415
416 if (empty($var)) {
417 return;
418 }
419 $value = isset($_POST[$var]) ? $_POST[$var] : $_GET[$var];
420 if (isset($value) && is_string($value)) {
421 $value = stripslashes($value);
422 } // Originally check '&& get_magic_quotes_gpc() ' but the values of $_GET are always slashed regardless of get_magic_quotes_gpc() because HTTP_POST/GET_VARS are run through addSlashesOnArray in the very beginning of index_ts.php eg.
423 if ($strip && isset($value) && is_array($value)) {
424 self::stripSlashesOnArray($value);
425 }
426 return $value;
427 }
428
429 /**
430 * Returns the global arrays $_GET and $_POST merged with $_POST taking precedence.
431 * Usage: 1
432 *
433 * @param string Key (variable name) from GET or POST vars
434 * @return array Returns the GET vars merged recursively onto the POST vars.
435 * @deprecated since TYPO3 3.7, will be removed in TYPO3 4.6 - Use t3lib_div::_GPmerged instead
436 * @see _GP()
437 */
438 public static function GParrayMerged($var) {
439 self::logDeprecatedFunction();
440
441 return self::_GPmerged($var);
442 }
443
444 /**
445 * Wrapper for the RemoveXSS function.
446 * Removes potential XSS code from an input string.
447 *
448 * Using an external class by Travis Puderbaugh <kallahar@quickwired.com>
449 *
450 * @param string Input string
451 * @return string Input string with potential XSS code removed
452 */
453 public static function removeXSS($string) {
454 require_once(PATH_typo3 . 'contrib/RemoveXSS/RemoveXSS.php');
455 $string = RemoveXSS::process($string);
456 return $string;
457 }
458
459
460 /*************************
461 *
462 * IMAGE FUNCTIONS
463 *
464 *************************/
465
466
467 /**
468 * Compressing a GIF file if not already LZW compressed.
469 * This function is a workaround for the fact that ImageMagick and/or GD does not compress GIF-files to their minimun size (that is RLE or no compression used)
470 *
471 * The function takes a file-reference, $theFile, and saves it again through GD or ImageMagick in order to compress the file
472 * GIF:
473 * If $type is not set, the compression is done with ImageMagick (provided that $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw'] is pointing to the path of a lzw-enabled version of 'convert') else with GD (should be RLE-enabled!)
474 * If $type is set to either 'IM' or 'GD' the compression is done with ImageMagick and GD respectively
475 * PNG:
476 * No changes.
477 *
478 * $theFile is expected to be a valid GIF-file!
479 * The function returns a code for the operation.
480 * Usage: 9
481 *
482 * @param string Filepath
483 * @param string See description of function
484 * @return string Returns "GD" if GD was used, otherwise "IM" if ImageMagick was used. If nothing done at all, it returns empty string.
485 */
486 public static function gif_compress($theFile, $type) {
487 $gfxConf = $GLOBALS['TYPO3_CONF_VARS']['GFX'];
488 $returnCode = '';
489 if ($gfxConf['gif_compress'] && strtolower(substr($theFile, -4, 4)) == '.gif') { // GIF...
490 if (($type == 'IM' || !$type) && $gfxConf['im'] && $gfxConf['im_path_lzw']) { // IM
491 $cmd = self::imageMagickCommand('convert', '"' . $theFile . '" "' . $theFile . '"', $gfxConf['im_path_lzw']);
492 t3lib_utility_Command::exec($cmd);
493 $returnCode = 'IM';
494 if (@is_file($theFile)) {
495 self::fixPermissions($theFile);
496 }
497 } elseif (($type == 'GD' || !$type) && $gfxConf['gdlib'] && !$gfxConf['gdlib_png']) { // GD
498 $tempImage = imageCreateFromGif($theFile);
499 imageGif($tempImage, $theFile);
500 imageDestroy($tempImage);
501 $returnCode = 'GD';
502 if (@is_file($theFile)) {
503 self::fixPermissions($theFile);
504 }
505 }
506 }
507 return $returnCode;
508 }
509
510 /**
511 * Converts a png file to gif.
512 * This converts a png file to gif IF the FLAG $GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif'] is set true.
513 * Usage: 5
514 *
515 * @param string $theFile the filename with path
516 * @return string new filename
517 */
518 public static function png_to_gif_by_imagemagick($theFile) {
519 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['png_to_gif']
520 && $GLOBALS['TYPO3_CONF_VARS']['GFX']['im']
521 && $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw']
522 && strtolower(substr($theFile, -4, 4)) == '.png'
523 && @is_file($theFile)) { // IM
524 $newFile = substr($theFile, 0, -4) . '.gif';
525 $cmd = self::imageMagickCommand('convert', '"' . $theFile . '" "' . $newFile . '"', $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path_lzw']);
526 t3lib_utility_Command::exec($cmd);
527 $theFile = $newFile;
528 if (@is_file($newFile)) {
529 self::fixPermissions($newFile);
530 }
531 // unlink old file?? May be bad idea bacause TYPO3 would then recreate the file every time as TYPO3 thinks the file is not generated because it's missing!! So do not unlink $theFile here!!
532 }
533 return $theFile;
534 }
535
536 /**
537 * Returns filename of the png/gif version of the input file (which can be png or gif).
538 * If input file type does not match the wanted output type a conversion is made and temp-filename returned.
539 * Usage: 2
540 *
541 * @param string Filepath of image file
542 * @param boolean If set, then input file is converted to PNG, otherwise to GIF
543 * @return string If the new image file exists, it's filepath is returned
544 */
545 public static function read_png_gif($theFile, $output_png = 0) {
546 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['im'] && @is_file($theFile)) {
547 $ext = strtolower(substr($theFile, -4, 4));
548 if (
549 ((string) $ext == '.png' && $output_png) ||
550 ((string) $ext == '.gif' && !$output_png)
551 ) {
552 return $theFile;
553 } else {
554 $newFile = PATH_site . 'typo3temp/readPG_' . md5($theFile . '|' . filemtime($theFile)) . ($output_png ? '.png' : '.gif');
555 $cmd = self::imageMagickCommand('convert', '"' . $theFile . '" "' . $newFile . '"', $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_path']);
556 t3lib_utility_Command::exec($cmd);
557 if (@is_file($newFile)) {
558 self::fixPermissions($newFile);
559 return $newFile;
560 }
561 }
562 }
563 }
564
565
566 /*************************
567 *
568 * STRING FUNCTIONS
569 *
570 *************************/
571
572 /**
573 * Truncates string.
574 * Returns a new string of max. $chars length.
575 * If the string is longer, it will be truncated and appended with '...'.
576 * Usage: 39
577 *
578 * @param string string to truncate
579 * @param integer must be an integer with an absolute value of at least 4. if negative the string is cropped from the right end.
580 * @param string String to append to the output if it is truncated, default is '...'
581 * @return string new string
582 * @deprecated since TYPO3 4.1, will be removed in TYPO3 4.6 - Works ONLY for single-byte charsets! Use t3lib_div::fixed_lgd_cs() instead
583 * @see fixed_lgd_pre()
584 */
585 public static function fixed_lgd($string, $origChars, $preStr = '...') {
586 self::logDeprecatedFunction();
587
588 $chars = abs($origChars);
589 if ($chars >= 4) {
590 if (strlen($string) > $chars) {
591 return $origChars < 0 ?
592 $preStr . trim(substr($string, -($chars - 3))) :
593 trim(substr($string, 0, $chars - 3)) . $preStr;
594 }
595 }
596 return $string;
597 }
598
599 /**
600 * Truncates string.
601 * Returns a new string of max. $chars length.
602 * If the string is longer, it will be truncated and prepended with '...'.
603 * This works like fixed_lgd(), but is truncated in the start of the string instead of the end
604 * Usage: 6
605 *
606 * @param string string to truncate
607 * @param integer must be an integer of at least 4
608 * @return string new string
609 * @deprecated since TYPO3 4.1, will be removed in TYPO3 4.6 - Use t3lib_div::fixed_lgd_cs() instead (with negative input value for $chars)
610 * @see fixed_lgd()
611 */
612 public static function fixed_lgd_pre($string, $chars) {
613 self::logDeprecatedFunction();
614
615 return strrev(self::fixed_lgd(strrev($string), $chars));
616 }
617
618 /**
619 * Truncates a string with appended/prepended "..." and takes current character set into consideration.
620 * Usage: 75
621 *
622 * @param string string to truncate
623 * @param integer must be an integer with an absolute value of at least 4. if negative the string is cropped from the right end.
624 * @param string appendix to the truncated string
625 * @return string cropped string
626 */
627 public static function fixed_lgd_cs($string, $chars, $appendString = '...') {
628 if (is_object($GLOBALS['LANG'])) {
629 return $GLOBALS['LANG']->csConvObj->crop($GLOBALS['LANG']->charSet, $string, $chars, $appendString);
630 } elseif (is_object($GLOBALS['TSFE'])) {
631 $charSet = ($GLOBALS['TSFE']->renderCharset != '' ? $GLOBALS['TSFE']->renderCharset : $GLOBALS['TSFE']->defaultCharSet);
632 return $GLOBALS['TSFE']->csConvObj->crop($charSet, $string, $chars, $appendString);
633 } else {
634 // this case should not happen
635 $csConvObj = self::makeInstance('t3lib_cs');
636 return $csConvObj->crop('iso-8859-1', $string, $chars, $appendString);
637 }
638 }
639
640 /**
641 * Breaks up the text for emails
642 * Usage: 1
643 *
644 * @param string The string to break up
645 * @param string The string to implode the broken lines with (default/typically \n)
646 * @param integer The line length
647 * @deprecated since TYPO3 4.1, will be removed in TYPO3 4.6 - Use PHP function wordwrap()
648 * @return string
649 */
650 public static function breakTextForEmail($str, $implChar = LF, $charWidth = 76) {
651 self::logDeprecatedFunction();
652
653 $lines = explode(LF, $str);
654 $outArr = array();
655 foreach ($lines as $lStr) {
656 $outArr[] = self::breakLinesForEmail($lStr, $implChar, $charWidth);
657 }
658 return implode(LF, $outArr);
659 }
660
661 /**
662 * Breaks up a single line of text for emails
663 * Usage: 5
664 *
665 * @param string The string to break up
666 * @param string The string to implode the broken lines with (default/typically \n)
667 * @param integer The line length
668 * @return string
669 * @see breakTextForEmail()
670 */
671 public static function breakLinesForEmail($str, $implChar = LF, $charWidth = 76) {
672 $lines = array();
673 $l = $charWidth;
674 $p = 0;
675 while (strlen($str) > $p) {
676 $substr = substr($str, $p, $l);
677 if (strlen($substr) == $l) {
678 $count = count(explode(' ', trim(strrev($substr))));
679 if ($count > 1) { // OK...
680 $parts = explode(' ', strrev($substr), 2);
681 $theLine = strrev($parts[1]);
682 } else {
683 $afterParts = explode(' ', substr($str, $l + $p), 2);
684 $theLine = $substr . $afterParts[0];
685 }
686 if (!strlen($theLine)) {
687 break;
688 } // Error, because this would keep us in an endless loop.
689 } else {
690 $theLine = $substr;
691 }
692
693 $lines[] = trim($theLine);
694 $p += strlen($theLine);
695 if (!trim(substr($str, $p, $l))) {
696 break;
697 } // added...
698 }
699 return implode($implChar, $lines);
700 }
701
702 /**
703 * Match IP number with list of numbers with wildcard
704 * Dispatcher method for switching into specialised IPv4 and IPv6 methods.
705 * Usage: 10
706 *
707 * @param string $baseIP is the current remote IP address for instance, typ. REMOTE_ADDR
708 * @param string $list is a comma-list of IP-addresses to match with. *-wildcard allowed instead of number, plus leaving out parts in the IP number is accepted as wildcard (eg. 192.168.*.* equals 192.168). If list is "*" no check is done and the function returns TRUE immediately. An empty list always returns FALSE.
709 * @return boolean True if an IP-mask from $list matches $baseIP
710 */
711 public static function cmpIP($baseIP, $list) {
712 $list = trim($list);
713 if ($list === '') {
714 return FALSE;
715 } elseif ($list === '*') {
716 return TRUE;
717 }
718 if (strpos($baseIP, ':') !== FALSE && self::validIPv6($baseIP)) {
719 return self::cmpIPv6($baseIP, $list);
720 } else {
721 return self::cmpIPv4($baseIP, $list);
722 }
723 }
724
725 /**
726 * Match IPv4 number with list of numbers with wildcard
727 *
728 * @param string $baseIP is the current remote IP address for instance, typ. REMOTE_ADDR
729 * @param string $list is a comma-list of IP-addresses to match with. *-wildcard allowed instead of number, plus leaving out parts in the IP number is accepted as wildcard (eg. 192.168.*.* equals 192.168)
730 * @return boolean True if an IP-mask from $list matches $baseIP
731 */
732 public static function cmpIPv4($baseIP, $list) {
733 $IPpartsReq = explode('.', $baseIP);
734 if (count($IPpartsReq) == 4) {
735 $values = self::trimExplode(',', $list, 1);
736
737 foreach ($values as $test) {
738 list($test, $mask) = explode('/', $test);
739
740 if (intval($mask)) {
741 // "192.168.3.0/24"
742 $lnet = ip2long($test);
743 $lip = ip2long($baseIP);
744 $binnet = str_pad(decbin($lnet), 32, '0', STR_PAD_LEFT);
745 $firstpart = substr($binnet, 0, $mask);
746 $binip = str_pad(decbin($lip), 32, '0', STR_PAD_LEFT);
747 $firstip = substr($binip, 0, $mask);
748 $yes = (strcmp($firstpart, $firstip) == 0);
749 } else {
750 // "192.168.*.*"
751 $IPparts = explode('.', $test);
752 $yes = 1;
753 foreach ($IPparts as $index => $val) {
754 $val = trim($val);
755 if (strcmp($val, '*') && strcmp($IPpartsReq[$index], $val)) {
756 $yes = 0;
757 }
758 }
759 }
760 if ($yes) {
761 return TRUE;
762 }
763 }
764 }
765 return FALSE;
766 }
767
768 /**
769 * Match IPv6 address with a list of IPv6 prefixes
770 *
771 * @param string $baseIP is the current remote IP address for instance
772 * @param string $list is a comma-list of IPv6 prefixes, could also contain IPv4 addresses
773 * @return boolean True if an baseIP matches any prefix
774 */
775 public static function cmpIPv6($baseIP, $list) {
776 $success = FALSE; // Policy default: Deny connection
777 $baseIP = self::normalizeIPv6($baseIP);
778
779 $values = self::trimExplode(',', $list, 1);
780 foreach ($values as $test) {
781 list($test, $mask) = explode('/', $test);
782 if (self::validIPv6($test)) {
783 $test = self::normalizeIPv6($test);
784 if (intval($mask)) {
785 switch ($mask) { // test on /48 /64
786 case '48':
787 $testBin = substr(self::IPv6Hex2Bin($test), 0, 48);
788 $baseIPBin = substr(self::IPv6Hex2Bin($baseIP), 0, 48);
789 $success = strcmp($testBin, $baseIPBin) == 0 ? TRUE : FALSE;
790 break;
791 case '64':
792 $testBin = substr(self::IPv6Hex2Bin($test), 0, 64);
793 $baseIPBin = substr(self::IPv6Hex2Bin($baseIP), 0, 64);
794 $success = strcmp($testBin, $baseIPBin) == 0 ? TRUE : FALSE;
795 break;
796 default:
797 $success = FALSE;
798 }
799 } else {
800 if (self::validIPv6($test)) { // test on full ip address 128 bits
801 $testBin = self::IPv6Hex2Bin($test);
802 $baseIPBin = self::IPv6Hex2Bin($baseIP);
803 $success = strcmp($testBin, $baseIPBin) == 0 ? TRUE : FALSE;
804 }
805 }
806 }
807 if ($success) {
808 return TRUE;
809 }
810 }
811 return FALSE;
812 }
813
814 /**
815 * [Describe function...]
816 *
817 * @param [type] $hex: ...
818 * @return [type] ...
819 */
820 public static function IPv6Hex2Bin($hex) {
821 $bin = '';
822 $hex = str_replace(':', '', $hex); // Replace colon to nothing
823 for ($i = 0; $i < strlen($hex); $i = $i + 2) {
824 $bin .= chr(hexdec(substr($hex, $i, 2)));
825 }
826 return $bin;
827 }
828
829 /**
830 * Normalize an IPv6 address to full length
831 *
832 * @param string Given IPv6 address
833 * @return string Normalized address
834 */
835 public static function normalizeIPv6($address) {
836 $normalizedAddress = '';
837 $stageOneAddress = '';
838
839 $chunks = explode('::', $address); // Count 2 if if address has hidden zero blocks
840 if (count($chunks) == 2) {
841 $chunksLeft = explode(':', $chunks[0]);
842 $chunksRight = explode(':', $chunks[1]);
843 $left = count($chunksLeft);
844 $right = count($chunksRight);
845
846 // Special case: leading zero-only blocks count to 1, should be 0
847 if ($left == 1 && strlen($chunksLeft[0]) == 0) {
848 $left = 0;
849 }
850
851 $hiddenBlocks = 8 - ($left + $right);
852 $hiddenPart = '';
853 $h = 0;
854 while ($h < $hiddenBlocks) {
855 $hiddenPart .= '0000:';
856 $h++;
857 }
858
859 if ($left == 0) {
860 $stageOneAddress = $hiddenPart . $chunks[1];
861 } else {
862 $stageOneAddress = $chunks[0] . ':' . $hiddenPart . $chunks[1];
863 }
864 } else {
865 $stageOneAddress = $address;
866 }
867
868 // normalize the blocks:
869 $blocks = explode(':', $stageOneAddress);
870 $divCounter = 0;
871 foreach ($blocks as $block) {
872 $tmpBlock = '';
873 $i = 0;
874 $hiddenZeros = 4 - strlen($block);
875 while ($i < $hiddenZeros) {
876 $tmpBlock .= '0';
877 $i++;
878 }
879 $normalizedAddress .= $tmpBlock . $block;
880 if ($divCounter < 7) {
881 $normalizedAddress .= ':';
882 $divCounter++;
883 }
884 }
885 return $normalizedAddress;
886 }
887
888 /**
889 * Validate a given IP address.
890 *
891 * Possible format are IPv4 and IPv6.
892 *
893 * @param string IP address to be tested
894 * @return boolean True if $ip is either of IPv4 or IPv6 format.
895 */
896 public static function validIP($ip) {
897 return (filter_var($ip, FILTER_VALIDATE_IP) !== FALSE);
898 }
899
900 /**
901 * Validate a given IP address to the IPv4 address format.
902 *
903 * Example for possible format: 10.0.45.99
904 *
905 * @param string IP address to be tested
906 * @return boolean True if $ip is of IPv4 format.
907 */
908 public static function validIPv4($ip) {
909 return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== FALSE);
910 }
911
912 /**
913 * Validate a given IP address to the IPv6 address format.
914 *
915 * Example for possible format: 43FB::BB3F:A0A0:0 | ::1
916 *
917 * @param string IP address to be tested
918 * @return boolean True if $ip is of IPv6 format.
919 */
920 public static function validIPv6($ip) {
921 return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== FALSE);
922 }
923
924 /**
925 * Match fully qualified domain name with list of strings with wildcard
926 *
927 * @param string The current remote IP address for instance, typ. REMOTE_ADDR
928 * @param string A comma-list of domain names to match with. *-wildcard allowed but cannot be part of a string, so it must match the full host name (eg. myhost.*.com => correct, myhost.*domain.com => wrong)
929 * @return boolean True if a domain name mask from $list matches $baseIP
930 */
931 public static function cmpFQDN($baseIP, $list) {
932 if (count(explode('.', $baseIP)) == 4) {
933 $resolvedHostName = explode('.', gethostbyaddr($baseIP));
934 $values = self::trimExplode(',', $list, 1);
935
936 foreach ($values as $test) {
937 $hostNameParts = explode('.', $test);
938 $yes = 1;
939
940 foreach ($hostNameParts as $index => $val) {
941 $val = trim($val);
942 if (strcmp($val, '*') && strcmp($resolvedHostName[$index], $val)) {
943 $yes = 0;
944 }
945 }
946 if ($yes) {
947 return TRUE;
948 }
949 }
950 }
951 return FALSE;
952 }
953
954 /**
955 * Checks if a given URL matches the host that currently handles this HTTP request.
956 * Scheme, hostname and (optional) port of the given URL are compared.
957 *
958 * @param string $url: URL to compare with the TYPO3 request host
959 * @return boolean Whether the URL matches the TYPO3 request host
960 */
961 public static function isOnCurrentHost($url) {
962 return (stripos($url . '/', self::getIndpEnv('TYPO3_REQUEST_HOST') . '/') === 0);
963 }
964
965 /**
966 * Check for item in list
967 * Check if an item exists in a comma-separated list of items.
968 * Usage: 163
969 *
970 * @param string comma-separated list of items (string)
971 * @param string item to check for
972 * @return boolean true if $item is in $list
973 */
974 public static function inList($list, $item) {
975 return (strpos(',' . $list . ',', ',' . $item . ',') !== FALSE ? TRUE : FALSE);
976 }
977
978 /**
979 * Removes an item from a comma-separated list of items.
980 * Usage: 1
981 *
982 * @param string element to remove
983 * @param string comma-separated list of items (string)
984 * @return string new comma-separated list of items
985 */
986 public static function rmFromList($element, $list) {
987 $items = explode(',', $list);
988 foreach ($items as $k => $v) {
989 if ($v == $element) {
990 unset($items[$k]);
991 }
992 }
993 return implode(',', $items);
994 }
995
996 /**
997 * Expand a comma-separated list of integers with ranges (eg 1,3-5,7 becomes 1,3,4,5,7).
998 * Ranges are limited to 1000 values per range.
999 *
1000 * @param string comma-separated list of integers with ranges (string)
1001 * @return string new comma-separated list of items
1002 * @author Martin Kutschker <martin.kutschker@activesolution.at>
1003 */
1004 public static function expandList($list) {
1005 $items = explode(',', $list);
1006 $list = array();
1007 foreach ($items as $item) {
1008 $range = explode('-', $item);
1009 if (isset($range[1])) {
1010 $runAwayBrake = 1000;
1011 for ($n = $range[0]; $n <= $range[1]; $n++) {
1012 $list[] = $n;
1013
1014 $runAwayBrake--;
1015 if ($runAwayBrake <= 0) {
1016 break;
1017 }
1018 }
1019 } else {
1020 $list[] = $item;
1021 }
1022 }
1023 return implode(',', $list);
1024 }
1025
1026 /**
1027 * Forces the integer $theInt into the boundaries of $min and $max. If the $theInt is 'false' then the $zeroValue is applied.
1028 * Usage: 224
1029 *
1030 * @param integer Input value
1031 * @param integer Lower limit
1032 * @param integer Higher limit
1033 * @param integer Default value if input is false.
1034 * @return integer The input value forced into the boundaries of $min and $max
1035 */
1036 public static function intInRange($theInt, $min, $max = 2000000000, $zeroValue = 0) {
1037 // Returns $theInt as an integer in the integerspace from $min to $max
1038 $theInt = intval($theInt);
1039 if ($zeroValue && !$theInt) {
1040 $theInt = $zeroValue;
1041 } // If the input value is zero after being converted to integer, zeroValue may set another default value for it.
1042 if ($theInt < $min) {
1043 $theInt = $min;
1044 }
1045 if ($theInt > $max) {
1046 $theInt = $max;
1047 }
1048 return $theInt;
1049 }
1050
1051 /**
1052 * Returns the $integer if greater than zero, otherwise returns zero.
1053 * Usage: 1
1054 *
1055 * @param integer Integer string to process
1056 * @return integer
1057 */
1058 public static function intval_positive($theInt) {
1059 $theInt = intval($theInt);
1060 if ($theInt < 0) {
1061 $theInt = 0;
1062 }
1063 return $theInt;
1064 }
1065
1066 /**
1067 * Returns an integer from a three part version number, eg '4.12.3' -> 4012003
1068 * Usage: 2
1069 *
1070 * @param string Version number on format x.x.x
1071 * @return integer Integer version of version number (where each part can count to 999)
1072 */
1073 public static function int_from_ver($verNumberStr) {
1074 $verParts = explode('.', $verNumberStr);
1075 return intval((int) $verParts[0] . str_pad((int) $verParts[1], 3, '0', STR_PAD_LEFT) . str_pad((int) $verParts[2], 3, '0', STR_PAD_LEFT));
1076 }
1077
1078 /**
1079 * Returns true if the current TYPO3 version (or compatibility version) is compatible to the input version
1080 * Notice that this function compares branches, not versions (4.0.1 would be > 4.0.0 although they use the same compat_version)
1081 *
1082 * @param string Minimum branch number required (format x.y / e.g. "4.0" NOT "4.0.0"!)
1083 * @return boolean Returns true if this setup is compatible with the provided version number
1084 * @todo Still needs a function to convert versions to branches
1085 */
1086 public static function compat_version($verNumberStr) {
1087 global $TYPO3_CONF_VARS;
1088 $currVersionStr = $TYPO3_CONF_VARS['SYS']['compat_version'] ? $TYPO3_CONF_VARS['SYS']['compat_version'] : TYPO3_branch;
1089
1090 if (self::int_from_ver($currVersionStr) < self::int_from_ver($verNumberStr)) {
1091 return FALSE;
1092 } else {
1093 return TRUE;
1094 }
1095 }
1096
1097 /**
1098 * Makes a positive integer hash out of the first 7 chars from the md5 hash of the input
1099 * Usage: 5
1100 *
1101 * @param string String to md5-hash
1102 * @return integer Returns 28bit integer-hash
1103 */
1104 public static function md5int($str) {
1105 return hexdec(substr(md5($str), 0, 7));
1106 }
1107
1108 /**
1109 * Returns the first 10 positions of the MD5-hash (changed from 6 to 10 recently)
1110 *
1111 * Usage: 37
1112 *
1113 * @param string Input string to be md5-hashed
1114 * @param integer The string-length of the output
1115 * @return string Substring of the resulting md5-hash, being $len chars long (from beginning)
1116 */
1117 public static function shortMD5($input, $len = 10) {
1118 return substr(md5($input), 0, $len);
1119 }
1120
1121 /**
1122 * Returns a proper HMAC on a given input string and secret TYPO3 encryption key.
1123 *
1124 * @param string Input string to create HMAC from
1125 * @return string resulting (hexadecimal) HMAC currently with a length of 40 (HMAC-SHA-1)
1126 */
1127 public static function hmac($input) {
1128 $hashAlgorithm = 'sha1';
1129 $hashBlocksize = 64;
1130 $hmac = '';
1131
1132 if (extension_loaded('hash') && function_exists('hash_hmac') && function_exists('hash_algos') && in_array($hashAlgorithm, hash_algos())) {
1133 $hmac = hash_hmac($hashAlgorithm, $input, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']);
1134 } else {
1135 // outer padding
1136 $opad = str_repeat(chr(0x5C), $hashBlocksize);
1137 // innner padding
1138 $ipad = str_repeat(chr(0x36), $hashBlocksize);
1139 if (strlen($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']) > $hashBlocksize) {
1140 // keys longer than blocksize are shorten
1141 $key = str_pad(pack('H*', call_user_func($hashAlgorithm, $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])), $hashBlocksize, chr(0x00));
1142 } else {
1143 // keys shorter than blocksize are zero-padded
1144 $key = str_pad($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'], $hashBlocksize, chr(0x00));
1145 }
1146 $hmac = call_user_func($hashAlgorithm, ($key ^ $opad) . pack('H*', call_user_func($hashAlgorithm, ($key ^ $ipad) . $input)));
1147 }
1148 return $hmac;
1149 }
1150
1151 /**
1152 * Takes comma-separated lists and arrays and removes all duplicates
1153 * If a value in the list is trim(empty), the value is ignored.
1154 * Usage: 16
1155 *
1156 * @param string Accept multiple parameters wich can be comma-separated lists of values and arrays.
1157 * @param mixed $secondParameter: Dummy field, which if set will show a warning!
1158 * @return string Returns the list without any duplicates of values, space around values are trimmed
1159 */
1160 public static function uniqueList($in_list, $secondParameter = NULL) {
1161 if (is_array($in_list)) {
1162 throw new InvalidArgumentException(
1163 'TYPO3 Fatal Error: t3lib_div::uniqueList() does NOT support array arguments anymore! Only string comma lists!',
1164 1270853885
1165 );
1166 }
1167 if (isset($secondParameter)) {
1168 throw new InvalidArgumentException(
1169 'TYPO3 Fatal Error: t3lib_div::uniqueList() does NOT support more than a single argument value anymore. You have specified more than one!',
1170 1270853886
1171 );
1172 }
1173
1174 return implode(',', array_unique(self::trimExplode(',', $in_list, 1)));
1175 }
1176
1177 /**
1178 * Splits a reference to a file in 5 parts
1179 * Usage: 43
1180 *
1181 * @param string Filename/filepath to be analysed
1182 * @return array Contains keys [path], [file], [filebody], [fileext], [realFileext]
1183 */
1184 public static function split_fileref($fileref) {
1185 $reg = array();
1186 if (preg_match('/(.*\/)(.*)$/', $fileref, $reg)) {
1187 $info['path'] = $reg[1];
1188 $info['file'] = $reg[2];
1189 } else {
1190 $info['path'] = '';
1191 $info['file'] = $fileref;
1192 }
1193
1194 $reg = '';
1195 if (!is_dir($fileref) && preg_match('/(.*)\.([^\.]*$)/', $info['file'], $reg)) {
1196 $info['filebody'] = $reg[1];
1197 $info['fileext'] = strtolower($reg[2]);
1198 $info['realFileext'] = $reg[2];
1199 } else {
1200 $info['filebody'] = $info['file'];
1201 $info['fileext'] = '';
1202 }
1203 reset($info);
1204 return $info;
1205 }
1206
1207 /**
1208 * Returns the directory part of a path without trailing slash
1209 * If there is no dir-part, then an empty string is returned.
1210 * Behaviour:
1211 *
1212 * '/dir1/dir2/script.php' => '/dir1/dir2'
1213 * '/dir1/' => '/dir1'
1214 * 'dir1/script.php' => 'dir1'
1215 * 'd/script.php' => 'd'
1216 * '/script.php' => ''
1217 * '' => ''
1218 * Usage: 5
1219 *
1220 * @param string Directory name / path
1221 * @return string Processed input value. See function description.
1222 */
1223 public static function dirname($path) {
1224 $p = self::revExplode('/', $path, 2);
1225 return count($p) == 2 ? $p[0] : '';
1226 }
1227
1228 /**
1229 * Modifies a HTML Hex color by adding/subtracting $R,$G and $B integers
1230 * Usage: 11
1231 *
1232 * @param string A hexadecimal color code, #xxxxxx
1233 * @param integer Offset value 0-255
1234 * @param integer Offset value 0-255
1235 * @param integer Offset value 0-255
1236 * @return string A hexadecimal color code, #xxxxxx, modified according to input vars
1237 * @see modifyHTMLColorAll()
1238 */
1239 public static function modifyHTMLColor($color, $R, $G, $B) {
1240 // This takes a hex-color (# included!) and adds $R, $G and $B to the HTML-color (format: #xxxxxx) and returns the new color
1241 $nR = self::intInRange(hexdec(substr($color, 1, 2)) + $R, 0, 255);
1242 $nG = self::intInRange(hexdec(substr($color, 3, 2)) + $G, 0, 255);
1243 $nB = self::intInRange(hexdec(substr($color, 5, 2)) + $B, 0, 255);
1244 return '#' .
1245 substr('0' . dechex($nR), -2) .
1246 substr('0' . dechex($nG), -2) .
1247 substr('0' . dechex($nB), -2);
1248 }
1249
1250 /**
1251 * Modifies a HTML Hex color by adding/subtracting $all integer from all R/G/B channels
1252 * Usage: 6
1253 *
1254 * @param string A hexadecimal color code, #xxxxxx
1255 * @param integer Offset value 0-255 for all three channels.
1256 * @return string A hexadecimal color code, #xxxxxx, modified according to input vars
1257 * @see modifyHTMLColor()
1258 */
1259 public static function modifyHTMLColorAll($color, $all) {
1260 return self::modifyHTMLColor($color, $all, $all, $all);
1261 }
1262
1263 /**
1264 * Removes comma (if present) in the end of string
1265 * Usage: 2
1266 *
1267 * @param string String from which the comma in the end (if any) will be removed.
1268 * @return string
1269 * @deprecated since TYPO3 4.5, will be removed in TYPO3 4.7 - Use rtrim() directly
1270 */
1271 public static function rm_endcomma($string) {
1272 self::logDeprecatedFunction();
1273
1274 return rtrim($string, ',');
1275 }
1276
1277 /**
1278 * strtoupper which converts danish (and other characters) characters as well
1279 * Usage: 0
1280 *
1281 * @param string String to process
1282 * @return string
1283 * @deprecated since TYPO3 3.5, will be removed in TYPO3 4.6 - Use t3lib_cs::conv_case() instead or for HTML output, wrap your content in <span class="uppercase">...</span>)
1284 * @ignore
1285 */
1286 public static function danish_strtoupper($string) {
1287 self::logDeprecatedFunction();
1288
1289 $value = strtoupper($string);
1290 return strtr($value, array(
1291 chr(225) => chr(193),
1292 chr(233) => chr(201),
1293 chr(250) => chr(218),
1294 chr(237) => chr(205),
1295 chr(226) => chr(196),
1296 chr(234) => chr(203),
1297 chr(251) => chr(220),
1298 chr(244) => chr(214),
1299 chr(238) => chr(207),
1300 chr(230) => chr(198),
1301 chr(248) => chr(216),
1302 chr(229) => chr(197),
1303 chr(228) => chr(196),
1304 chr(246) => chr(214),
1305 chr(252) => chr(220),
1306 ));
1307 }
1308
1309 /**
1310 * Change umlaut characters to plain ASCII with normally two character target
1311 * Only known characters will be converted, so don't expect a result for any character.
1312 *
1313 * ä => ae, Ö => Oe
1314 *
1315 * @param string String to convert.
1316 * @deprecated since TYPO3 4.1, will be removed in TYPO3 4.6 - Works only for western europe single-byte charsets! Use t3lib_cs::specCharsToASCII() instead!
1317 * @return string
1318 */
1319 public static function convUmlauts($str) {
1320 self::logDeprecatedFunction();
1321
1322 $pattern = array(chr(228), chr(196), chr(246), chr(214), chr(252), chr(220), chr(223), chr(229), chr(197), chr(248), chr(216), chr(230), chr(198));
1323 $replace = array('ae', 'Ae', 'oe', 'Oe', 'ue', 'Ue', 'ss', 'aa', 'AA', 'oe', 'OE', 'ae', 'AE');
1324 return str_replace($pattern, $replace, $str);
1325 }
1326
1327 /**
1328 * Tests if the input can be interpreted as integer.
1329 *
1330 * @param mixed Any input variable to test
1331 * @return boolean Returns true if string is an integer
1332 */
1333 public static function testInt($var) {
1334 if ($var === '') {
1335 return FALSE;
1336 }
1337 return (string) intval($var) === (string) $var;
1338 }
1339
1340 /**
1341 * Returns true if the first part of $str matches the string $partStr
1342 * Usage: 59
1343 *
1344 * @param string Full string to check
1345 * @param string Reference string which must be found as the "first part" of the full string
1346 * @return boolean True if $partStr was found to be equal to the first part of $str
1347 */
1348 public static function isFirstPartOfStr($str, $partStr) {
1349 return $partStr != '' && strpos((string) $str, (string) $partStr, 0) === 0;
1350 }
1351
1352 /**
1353 * Formats the input integer $sizeInBytes as bytes/kilobytes/megabytes (-/K/M)
1354 * Usage: 53
1355 *
1356 * @param integer Number of bytes to format.
1357 * @param string Labels for bytes, kilo, mega and giga separated by vertical bar (|) and possibly encapsulated in "". Eg: " | K| M| G" (which is the default value)
1358 * @return string Formatted representation of the byte number, for output.
1359 */
1360 public static function formatSize($sizeInBytes, $labels = '') {
1361
1362 // Set labels:
1363 if (strlen($labels) == 0) {
1364 $labels = ' | K| M| G';
1365 } else {
1366 $labels = str_replace('"', '', $labels);
1367 }
1368 $labelArr = explode('|', $labels);
1369
1370 // Find size:
1371 if ($sizeInBytes > 900) {
1372 if ($sizeInBytes > 900000000) { // GB
1373 $val = $sizeInBytes / (1024 * 1024 * 1024);
1374 return number_format($val, (($val < 20) ? 1 : 0), '.', '') . $labelArr[3];
1375 }
1376 elseif ($sizeInBytes > 900000) { // MB
1377 $val = $sizeInBytes / (1024 * 1024);
1378 return number_format($val, (($val < 20) ? 1 : 0), '.', '') . $labelArr[2];
1379 } else { // KB
1380 $val = $sizeInBytes / (1024);
1381 return number_format($val, (($val < 20) ? 1 : 0), '.', '') . $labelArr[1];
1382 }
1383 } else { // Bytes
1384 return $sizeInBytes . $labelArr[0];
1385 }
1386 }
1387
1388 /**
1389 * Returns microtime input to milliseconds
1390 * Usage: 2
1391 *
1392 * @param string Microtime
1393 * @return integer Microtime input string converted to an integer (milliseconds)
1394 */
1395 public static function convertMicrotime($microtime) {
1396 $parts = explode(' ', $microtime);
1397 return round(($parts[0] + $parts[1]) * 1000);
1398 }
1399
1400 /**
1401 * This splits a string by the chars in $operators (typical /+-*) and returns an array with them in
1402 * Usage: 2
1403 *
1404 * @param string Input string, eg "123 + 456 / 789 - 4"
1405 * @param string Operators to split by, typically "/+-*"
1406 * @return array Array with operators and operands separated.
1407 * @see tslib_cObj::calc(), tslib_gifBuilder::calcOffset()
1408 */
1409 public static function splitCalc($string, $operators) {
1410 $res = Array();
1411 $sign = '+';
1412 while ($string) {
1413 $valueLen = strcspn($string, $operators);
1414 $value = substr($string, 0, $valueLen);
1415 $res[] = Array($sign, trim($value));
1416 $sign = substr($string, $valueLen, 1);
1417 $string = substr($string, $valueLen + 1);
1418 }
1419 reset($res);
1420 return $res;
1421 }
1422
1423 /**
1424 * Calculates the input by +,-,*,/,%,^ with priority to + and -
1425 * Usage: 1
1426 *
1427 * @param string Input string, eg "123 + 456 / 789 - 4"
1428 * @return integer Calculated value. Or error string.
1429 * @see calcParenthesis()
1430 */
1431 public static function calcPriority($string) {
1432 $string = preg_replace('/[[:space:]]*/', '', $string); // removing all whitespace
1433 $string = '+' . $string; // Ensuring an operator for the first entrance
1434 $qm = '\*\/\+-^%';
1435 $regex = '([' . $qm . '])([' . $qm . ']?[0-9\.]*)';
1436 // split the expression here:
1437 $reg = array();
1438 preg_match_all('/' . $regex . '/', $string, $reg);
1439
1440 reset($reg[2]);
1441 $number = 0;
1442 $Msign = '+';
1443 $err = '';
1444 $buffer = doubleval(current($reg[2]));
1445 next($reg[2]); // Advance pointer
1446
1447 while (list($k, $v) = each($reg[2])) {
1448 $v = doubleval($v);
1449 $sign = $reg[1][$k];
1450 if ($sign == '+' || $sign == '-') {
1451 $number = $Msign == '-' ? $number -= $buffer : $number += $buffer;
1452 $Msign = $sign;
1453 $buffer = $v;
1454 } else {
1455 if ($sign == '/') {
1456 if ($v) {
1457 $buffer /= $v;
1458 } else {
1459 $err = 'dividing by zero';
1460 }
1461 }
1462 if ($sign == '%') {
1463 if ($v) {
1464 $buffer %= $v;
1465 } else {
1466 $err = 'dividing by zero';
1467 }
1468 }
1469 if ($sign == '*') {
1470 $buffer *= $v;
1471 }
1472 if ($sign == '^') {
1473 $buffer = pow($buffer, $v);
1474 }
1475 }
1476 }
1477 $number = $Msign == '-' ? $number -= $buffer : $number += $buffer;
1478 return $err ? 'ERROR: ' . $err : $number;
1479 }
1480
1481 /**
1482 * Calculates the input with parenthesis levels
1483 * Usage: 2
1484 *
1485 * @param string Input string, eg "(123 + 456) / 789 - 4"
1486 * @return integer Calculated value. Or error string.
1487 * @see calcPriority(), tslib_cObj::stdWrap()
1488 */
1489 public static function calcParenthesis($string) {
1490 $securC = 100;
1491 do {
1492 $valueLenO = strcspn($string, '(');
1493 $valueLenC = strcspn($string, ')');
1494 if ($valueLenC == strlen($string) || $valueLenC < $valueLenO) {
1495 $value = self::calcPriority(substr($string, 0, $valueLenC));
1496 $string = $value . substr($string, $valueLenC + 1);
1497 return $string;
1498 } else {
1499 $string = substr($string, 0, $valueLenO) . self::calcParenthesis(substr($string, $valueLenO + 1));
1500 }
1501 // Security:
1502 $securC--;
1503 if ($securC <= 0) {
1504 break;
1505 }
1506 } while ($valueLenO < strlen($string));
1507 return $string;
1508 }
1509
1510 /**
1511 * Inverse version of htmlspecialchars()
1512 * Usage: 4
1513 *
1514 * @param string Value where &gt;, &lt;, &quot; and &amp; should be converted to regular chars.
1515 * @return string Converted result.
1516 */
1517 public static function htmlspecialchars_decode($value) {
1518 $value = str_replace('&gt;', '>', $value);
1519 $value = str_replace('&lt;', '<', $value);
1520 $value = str_replace('&quot;', '"', $value);
1521 $value = str_replace('&amp;', '&', $value);
1522 return $value;
1523 }
1524
1525 /**
1526 * Re-converts HTML entities if they have been converted by htmlspecialchars()
1527 * Usage: 10
1528 *
1529 * @param string String which contains eg. "&amp;amp;" which should stay "&amp;". Or "&amp;#1234;" to "&#1234;". Or "&amp;#x1b;" to "&#x1b;"
1530 * @return string Converted result.
1531 */
1532 public static function deHSCentities($str) {
1533 return preg_replace('/&amp;([#[:alnum:]]*;)/', '&\1', $str);
1534 }
1535
1536 /**
1537 * This function is used to escape any ' -characters when transferring text to JavaScript!
1538 * Usage: 3
1539 *
1540 * @param string String to escape
1541 * @param boolean If set, also backslashes are escaped.
1542 * @param string The character to escape, default is ' (single-quote)
1543 * @return string Processed input string
1544 */
1545 public static function slashJS($string, $extended = 0, $char = "'") {
1546 if ($extended) {
1547 $string = str_replace("\\", "\\\\", $string);
1548 }
1549 return str_replace($char, "\\" . $char, $string);
1550 }
1551
1552 /**
1553 * Version of rawurlencode() where all spaces (%20) are re-converted to space-characters.
1554 * Usefull when passing text to JavaScript where you simply url-encode it to get around problems with syntax-errors, linebreaks etc.
1555 * Usage: 4
1556 *
1557 * @param string String to raw-url-encode with spaces preserved
1558 * @return string Rawurlencoded result of input string, but with all %20 (space chars) converted to real spaces.
1559 */
1560 public static function rawUrlEncodeJS($str) {
1561 return str_replace('%20', ' ', rawurlencode($str));
1562 }
1563
1564 /**
1565 * rawurlencode which preserves "/" chars
1566 * Usefull when filepaths should keep the "/" chars, but have all other special chars encoded.
1567 * Usage: 5
1568 *
1569 * @param string Input string
1570 * @return string Output string
1571 */
1572 public static function rawUrlEncodeFP($str) {
1573 return str_replace('%2F', '/', rawurlencode($str));
1574 }
1575
1576 /**
1577 * Checking syntax of input email address
1578 * Usage: 5
1579 *
1580 * @param string Input string to evaluate
1581 * @return boolean Returns true if the $email address (input string) is valid
1582 */
1583 public static function validEmail($email) {
1584 // enforce maximum length to prevent libpcre recursion crash bug #52929 in PHP
1585 // fixed in PHP 5.2+ later than Sept 2010; length restriction per SMTP RFC 2821
1586 if (strlen($email) > 320) {
1587 return FALSE;
1588 }
1589 return (filter_var($email, FILTER_VALIDATE_EMAIL) !== FALSE);
1590 }
1591
1592 /**
1593 * Checks if current e-mail sending method does not accept recipient/sender name
1594 * in a call to PHP mail() function. Windows version of mail() and mini_sendmail
1595 * program are known not to process such input correctly and they cause SMTP
1596 * errors. This function will return true if current mail sending method has
1597 * problem with recipient name in recipient/sender argument for mail().
1598 *
1599 * TODO: 4.3 should have additional configuration variable, which is combined
1600 * by || with the rest in this function.
1601 *
1602 * @return boolean true if mail() does not accept recipient name
1603 */
1604 public static function isBrokenEmailEnvironment() {
1605 return TYPO3_OS == 'WIN' || (FALSE !== strpos(ini_get('sendmail_path'), 'mini_sendmail'));
1606 }
1607
1608 /**
1609 * Changes from/to arguments for mail() function to work in any environment.
1610 *
1611 * @param string $address Address to adjust
1612 * @return string Adjusted address
1613 * @see t3lib_::isBrokenEmailEnvironment()
1614 */
1615 public static function normalizeMailAddress($address) {
1616 if (self::isBrokenEmailEnvironment() && FALSE !== ($pos1 = strrpos($address, '<'))) {
1617 $pos2 = strpos($address, '>', $pos1);
1618 $address = substr($address, $pos1 + 1, ($pos2 ? $pos2 : strlen($address)) - $pos1 - 1);
1619 }
1620 return $address;
1621 }
1622
1623 /**
1624 * Formats a string for output between <textarea>-tags
1625 * All content outputted in a textarea form should be passed through this function
1626 * Not only is the content htmlspecialchar'ed on output but there is also a single newline added in the top. The newline is necessary because browsers will ignore the first newline after <textarea> if that is the first character. Therefore better set it!
1627 * Usage: 23
1628 *
1629 * @param string Input string to be formatted.
1630 * @return string Formatted for <textarea>-tags
1631 */
1632 public static function formatForTextarea($content) {
1633 return LF . htmlspecialchars($content);
1634 }
1635
1636 /**
1637 * Converts string to uppercase
1638 * The function converts all Latin characters (a-z, but no accents, etc) to
1639 * uppercase. It is safe for all supported character sets (incl. utf-8).
1640 * Unlike strtoupper() it does not honour the locale.
1641 *
1642 * @param string Input string
1643 * @return string Uppercase String
1644 */
1645 public static function strtoupper($str) {
1646 return strtr((string) $str, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
1647 }
1648
1649 /**
1650 * Converts string to lowercase
1651 * The function converts all Latin characters (A-Z, but no accents, etc) to
1652 * lowercase. It is safe for all supported character sets (incl. utf-8).
1653 * Unlike strtolower() it does not honour the locale.
1654 *
1655 * @param string Input string
1656 * @return string Lowercase String
1657 */
1658 public static function strtolower($str) {
1659 return strtr((string) $str, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
1660 }
1661
1662 /**
1663 * Returns a string of highly randomized bytes (over the full 8-bit range).
1664 *
1665 * @copyright Drupal CMS
1666 * @license GNU General Public License version 2
1667 * @param integer Number of characters (bytes) to return
1668 * @return string Random Bytes
1669 */
1670 public static function generateRandomBytes($count) {
1671 $output = '';
1672 // /dev/urandom is available on many *nix systems and is considered
1673 // the best commonly available pseudo-random source.
1674 if (TYPO3_OS != 'WIN' && ($fh = @fopen('/dev/urandom', 'rb'))) {
1675 $output = fread($fh, $count);
1676 fclose($fh);
1677 } elseif (TYPO3_OS == 'WIN') {
1678 if (class_exists('COM')) {
1679 try {
1680 $com = new COM('CAPICOM.Utilities.1');
1681 $output = base64_decode($com->GetRandom($count, 0));
1682 } catch (Exception $e) {
1683 // CAPICOM not installed
1684 }
1685 }
1686 if ($output === '' && version_compare(PHP_VERSION, '5.3.0', '>=')) {
1687 if (function_exists('mcrypt_create_iv')) {
1688 $output = mcrypt_create_iv($count, MCRYPT_DEV_URANDOM);
1689 } elseif (function_exists('openssl_random_pseudo_bytes')) {
1690 $isStrong = NULL;
1691 $output = openssl_random_pseudo_bytes($count, $isStrong);
1692 // skip ssl since it wasn't using the strong algo
1693 if ($isStrong !== TRUE) {
1694 $output = '';
1695 }
1696 }
1697 }
1698 }
1699
1700 // fallback if other random byte generation failed until now
1701 if (!isset($output{$count - 1})) {
1702 // We initialize with the somewhat random.
1703 $randomState = $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']
1704 . base_convert(memory_get_usage() % pow(10, 6), 10, 2)
1705 . microtime() . uniqid('') . getmypid();
1706 while (!isset($output{$count - 1})) {
1707 $randomState = sha1(microtime() . mt_rand() . $randomState);
1708 $output .= sha1(mt_rand() . $randomState, TRUE);
1709 }
1710 $output = substr($output, strlen($output) - $count, $count);
1711 }
1712 return $output;
1713 }
1714
1715 /**
1716 * Returns a hex representation of a random byte string.
1717 *
1718 * @param integer Number of hex characters to return
1719 * @return string Random Bytes
1720 */
1721 public static function getRandomHexString($count) {
1722 return substr(bin2hex(self::generateRandomBytes(intval(($count + 1) / 2))), 0, $count);
1723 }
1724
1725 /**
1726 * Returns a given string with underscores as UpperCamelCase.
1727 * Example: Converts blog_example to BlogExample
1728 *
1729 * @param string $string: String to be converted to camel case
1730 * @return string UpperCamelCasedWord
1731 */
1732 public static function underscoredToUpperCamelCase($string) {
1733 $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string))));
1734 return $upperCamelCase;
1735 }
1736
1737 /**
1738 * Returns a given string with underscores as lowerCamelCase.
1739 * Example: Converts minimal_value to minimalValue
1740 *
1741 * @param string $string: String to be converted to camel case
1742 * @return string lowerCamelCasedWord
1743 */
1744 public static function underscoredToLowerCamelCase($string) {
1745 $upperCamelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', self::strtolower($string))));
1746 $lowerCamelCase = self::lcfirst($upperCamelCase);
1747 return $lowerCamelCase;
1748 }
1749
1750 /**
1751 * Returns a given CamelCasedString as an lowercase string with underscores.
1752 * Example: Converts BlogExample to blog_example, and minimalValue to minimal_value
1753 *
1754 * @param string $string: String to be converted to lowercase underscore
1755 * @return string lowercase_and_underscored_string
1756 */
1757 public static function camelCaseToLowerCaseUnderscored($string) {
1758 return self::strtolower(preg_replace('/(?<=\w)([A-Z])/', '_\\1', $string));
1759 }
1760
1761 /**
1762 * Converts the first char of a string to lowercase if it is a latin character (A-Z).
1763 * Example: Converts "Hello World" to "hello World"
1764 *
1765 * @param string $string: The string to be used to lowercase the first character
1766 * @return string The string with the first character as lowercase
1767 */
1768 public static function lcfirst($string) {
1769 return self::strtolower(substr($string, 0, 1)) . substr($string, 1);
1770 }
1771
1772 /**
1773 * Checks if a given string is a Uniform Resource Locator (URL).
1774 *
1775 * @param string $url: The URL to be validated
1776 * @return boolean Whether the given URL is valid
1777 */
1778 public static function isValidUrl($url) {
1779 return (filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED) !== FALSE);
1780 }
1781
1782
1783 /*************************
1784 *
1785 * ARRAY FUNCTIONS
1786 *
1787 *************************/
1788
1789 /**
1790 * Check if an string item exists in an array.
1791 * Please note that the order of function parameters is reverse compared to the PHP function in_array()!!!
1792 *
1793 * Comparison to PHP in_array():
1794 * -> $array = array(0, 1, 2, 3);
1795 * -> variant_a := t3lib_div::inArray($array, $needle)
1796 * -> variant_b := in_array($needle, $array)
1797 * -> variant_c := in_array($needle, $array, true)
1798 * +---------+-----------+-----------+-----------+
1799 * | $needle | variant_a | variant_b | variant_c |
1800 * +---------+-----------+-----------+-----------+
1801 * | '1a' | false | true | false |
1802 * | '' | false | true | false |
1803 * | '0' | true | true | false |
1804 * | 0 | true | true | true |
1805 * +---------+-----------+-----------+-----------+
1806 * Usage: 3
1807 *
1808 * @param array one-dimensional array of items
1809 * @param string item to check for
1810 * @return boolean true if $item is in the one-dimensional array $in_array
1811 */
1812 public static function inArray(array $in_array, $item) {
1813 foreach ($in_array as $val) {
1814 if (!is_array($val) && !strcmp($val, $item)) {
1815 return TRUE;
1816 }
1817 }
1818 return FALSE;
1819 }
1820
1821 /**
1822 * Explodes a $string delimited by $delim and passes each item in the array through intval().
1823 * Corresponds to t3lib_div::trimExplode(), but with conversion to integers for all values.
1824 * Usage: 76
1825 *
1826 * @param string Delimiter string to explode with
1827 * @param string The string to explode
1828 * @param boolean If set, all empty values (='') will NOT be set in output
1829 * @param integer If positive, the result will contain a maximum of limit elements,
1830 * if negative, all components except the last -limit are returned,
1831 * if zero (default), the result is not limited at all
1832 * @return array Exploded values, all converted to integers
1833 */
1834 public static function intExplode($delimiter, $string, $onlyNonEmptyValues = FALSE, $limit = 0) {
1835 $explodedValues = self::trimExplode($delimiter, $string, $onlyNonEmptyValues, $limit);
1836 return array_map('intval', $explodedValues);
1837 }
1838
1839 /**
1840 * Reverse explode which explodes the string counting from behind.
1841 * Thus t3lib_div::revExplode(':','my:words:here',2) will return array('my:words','here')
1842 * Usage: 8
1843 *
1844 * @param string Delimiter string to explode with
1845 * @param string The string to explode
1846 * @param integer Number of array entries
1847 * @return array Exploded values
1848 */
1849 public static function revExplode($delimiter, $string, $count = 0) {
1850 $explodedValues = explode($delimiter, strrev($string), $count);
1851 $explodedValues = array_map('strrev', $explodedValues);
1852 return array_reverse($explodedValues);
1853 }
1854
1855 /**
1856 * Explodes a string and trims all values for whitespace in the ends.
1857 * If $onlyNonEmptyValues is set, then all blank ('') values are removed.
1858 * Usage: 256
1859 *
1860 * @param string Delimiter string to explode with
1861 * @param string The string to explode
1862 * @param boolean If set, all empty values will be removed in output
1863 * @param integer If positive, the result will contain a maximum of
1864 * $limit elements, if negative, all components except
1865 * the last -$limit are returned, if zero (default),
1866 * the result is not limited at all. Attention though
1867 * that the use of this parameter can slow down this
1868 * function.
1869 * @return array Exploded values
1870 */
1871 public static function trimExplode($delim, $string, $removeEmptyValues = FALSE, $limit = 0) {
1872 $explodedValues = explode($delim, $string);
1873
1874 $result = array_map('trim', $explodedValues);
1875
1876 if ($removeEmptyValues) {
1877 $temp = array();
1878 foreach ($result as $value) {
1879 if ($value !== '') {
1880 $temp[] = $value;
1881 }
1882 }
1883 $result = $temp;
1884 }
1885
1886 if ($limit != 0) {
1887 if ($limit < 0) {
1888 $result = array_slice($result, 0, $limit);
1889 } elseif (count($result) > $limit) {
1890 $lastElements = array_slice($result, $limit - 1);
1891 $result = array_slice($result, 0, $limit - 1);
1892 $result[] = implode($delim, $lastElements);
1893 }
1894 }
1895
1896 return $result;
1897 }
1898
1899 /**
1900 * Remove duplicate values from an array
1901 * Usage: 0
1902 *
1903 * @param array Array of values to make unique
1904 * @return array
1905 * @ignore
1906 * @deprecated since TYPO3 3.5, will be removed in TYPO3 4.6 - Use the PHP function array_unique instead
1907 */
1908 public static function uniqueArray(array $valueArray) {
1909 self::logDeprecatedFunction();
1910
1911 return array_unique($valueArray);
1912 }
1913
1914 /**
1915 * Removes the value $cmpValue from the $array if found there. Returns the modified array
1916 * Usage: 3
1917 *
1918 * @param array Array containing the values
1919 * @param string Value to search for and if found remove array entry where found.
1920 * @return array Output array with entries removed if search string is found
1921 */
1922 public static function removeArrayEntryByValue(array $array, $cmpValue) {
1923 foreach ($array as $k => $v) {
1924 if (is_array($v)) {
1925 $array[$k] = self::removeArrayEntryByValue($v, $cmpValue);
1926 } elseif (!strcmp($v, $cmpValue)) {
1927 unset($array[$k]);
1928 }
1929 }
1930 return $array;
1931 }
1932
1933 /**
1934 * Filters an array to reduce its elements to match the condition.
1935 * The values in $keepItems can be optionally evaluated by a custom callback function.
1936 *
1937 * Example (arguments used to call this function):
1938 * $array = array(
1939 * array('aa' => array('first', 'second'),
1940 * array('bb' => array('third', 'fourth'),
1941 * array('cc' => array('fifth', 'sixth'),
1942 * );
1943 * $keepItems = array('third');
1944 * $getValueFunc = create_function('$value', 'return $value[0];');
1945 *
1946 * Returns:
1947 * array(
1948 * array('bb' => array('third', 'fourth'),
1949 * )
1950 *
1951 * @param array $array: The initial array to be filtered/reduced
1952 * @param mixed $keepItems: The items which are allowed/kept in the array - accepts array or csv string
1953 * @param string $getValueFunc: (optional) Unique function name set by create_function() used to get the value to keep
1954 * @return array The filtered/reduced array with the kept items
1955 */
1956 public static function keepItemsInArray(array $array, $keepItems, $getValueFunc = NULL) {
1957 if ($array) {
1958 // Convert strings to arrays:
1959 if (is_string($keepItems)) {
1960 $keepItems = self::trimExplode(',', $keepItems);
1961 }
1962 // create_function() returns a string:
1963 if (!is_string($getValueFunc)) {
1964 $getValueFunc = NULL;
1965 }
1966 // Do the filtering:
1967 if (is_array($keepItems) && count($keepItems)) {
1968 foreach ($array as $key => $value) {
1969 // Get the value to compare by using the callback function:
1970 $keepValue = (isset($getValueFunc) ? $getValueFunc($value) : $value);
1971 if (!in_array($keepValue, $keepItems)) {
1972 unset($array[$key]);
1973 }
1974 }
1975 }
1976 }
1977 return $array;
1978 }
1979
1980 /**
1981 * Implodes a multidim-array into GET-parameters (eg. &param[key][key2]=value2&param[key][key3]=value3)
1982 * Usage: 24
1983 *
1984 * @param string Name prefix for entries. Set to blank if you wish none.
1985 * @param array The (multidim) array to implode
1986 * @param string (keep blank)
1987 * @param boolean If set, parameters which were blank strings would be removed.
1988 * @param boolean If set, the param name itself (for example "param[key][key2]") would be rawurlencoded as well.
1989 * @return string Imploded result, fx. &param[key][key2]=value2&param[key][key3]=value3
1990 * @see explodeUrl2Array()
1991 */
1992 public static function implodeArrayForUrl($name, array $theArray, $str = '', $skipBlank = FALSE, $rawurlencodeParamName = FALSE) {
1993 foreach ($theArray as $Akey => $AVal) {
1994 $thisKeyName = $name ? $name . '[' . $Akey . ']' : $Akey;
1995 if (is_array($AVal)) {
1996 $str = self::implodeArrayForUrl($thisKeyName, $AVal, $str, $skipBlank, $rawurlencodeParamName);
1997 } else {
1998 if (!$skipBlank || strcmp($AVal, '')) {
1999 $str .= '&' . ($rawurlencodeParamName ? rawurlencode($thisKeyName) : $thisKeyName) .
2000 '=' . rawurlencode($AVal);
2001 }
2002 }
2003 }
2004 return $str;
2005 }
2006
2007 /**
2008 * Explodes a string with GETvars (eg. "&id=1&type=2&ext[mykey]=3") into an array
2009 *
2010 * @param string GETvars string
2011 * @param boolean If set, the string will be parsed into a multidimensional array if square brackets are used in variable names (using PHP function parse_str())
2012 * @return array Array of values. All values AND keys are rawurldecoded() as they properly should be. But this means that any implosion of the array again must rawurlencode it!
2013 * @see implodeArrayForUrl()
2014 */
2015 public static function explodeUrl2Array($string, $multidim = FALSE) {
2016 $output = array();
2017 if ($multidim) {
2018 parse_str($string, $output);
2019 } else {
2020 $p = explode('&', $string);
2021 foreach ($p as $v) {
2022 if (strlen($v)) {
2023 list($pK, $pV) = explode('=', $v, 2);
2024 $output[rawurldecode($pK)] = rawurldecode($pV);
2025 }
2026 }
2027 }
2028 return $output;
2029 }
2030
2031 /**
2032 * Returns an array with selected keys from incoming data.
2033 * (Better read source code if you want to find out...)
2034 * Usage: 3
2035 *
2036 * @param string List of variable/key names
2037 * @param array Array from where to get values based on the keys in $varList
2038 * @param boolean If set, then t3lib_div::_GP() is used to fetch the value if not found (isset) in the $getArray
2039 * @return array Output array with selected variables.
2040 */
2041 public static function compileSelectedGetVarsFromArray($varList, array $getArray, $GPvarAlt = 1) {
2042 $keys = self::trimExplode(',', $varList, 1);
2043 $outArr = array();
2044 foreach ($keys as $v) {
2045 if (isset($getArray[$v])) {
2046 $outArr[$v] = $getArray[$v];
2047 } elseif ($GPvarAlt) {
2048 $outArr[$v] = self::_GP($v);
2049 }
2050 }
2051 return $outArr;
2052 }
2053
2054 /**
2055 * AddSlash array
2056 * This function traverses a multidimentional array and adds slashes to the values.
2057 * NOTE that the input array is and argument by reference.!!
2058 * Twin-function to stripSlashesOnArray
2059 * Usage: 8
2060 *
2061 * @param array Multidimensional input array, (REFERENCE!)
2062 * @return array
2063 */
2064 public static function addSlashesOnArray(array &$theArray) {
2065 foreach ($theArray as &$value) {
2066 if (is_array($value)) {
2067 self::addSlashesOnArray($value);
2068 } else {
2069 $value = addslashes($value);
2070 }
2071 unset($value);
2072 }
2073 reset($theArray);
2074 }
2075
2076 /**
2077 * StripSlash array
2078 * This function traverses a multidimentional array and strips slashes to the values.
2079 * NOTE that the input array is and argument by reference.!!
2080 * Twin-function to addSlashesOnArray
2081 * Usage: 10
2082 *
2083 * @param array Multidimensional input array, (REFERENCE!)
2084 * @return array
2085 */
2086 public static function stripSlashesOnArray(array &$theArray) {
2087 foreach ($theArray as &$value) {
2088 if (is_array($value)) {
2089 self::stripSlashesOnArray($value);
2090 } else {
2091 $value = stripslashes($value);
2092 }
2093 unset($value);
2094 }
2095 reset($theArray);
2096 }
2097
2098 /**
2099 * Either slashes ($cmd=add) or strips ($cmd=strip) array $arr depending on $cmd
2100 * Usage: 0
2101 *
2102 * @param array Multidimensional input array
2103 * @param string "add" or "strip", depending on usage you wish.
2104 * @return array
2105 */
2106 public static function slashArray(array $arr, $cmd) {
2107 if ($cmd == 'strip') {
2108 self::stripSlashesOnArray($arr);
2109 }
2110 if ($cmd == 'add') {
2111 self::addSlashesOnArray($arr);
2112 }
2113 return $arr;
2114 }
2115
2116 /**
2117 * Rename Array keys with a given mapping table
2118 * @param array Array by reference which should be remapped
2119 * @param array Array with remap information, array/$oldKey => $newKey)
2120 */
2121 function remapArrayKeys(&$array, $mappingTable) {
2122 if (is_array($mappingTable)) {
2123 foreach ($mappingTable as $old => $new) {
2124 if ($new && isset($array[$old])) {
2125 $array[$new] = $array[$old];
2126 unset ($array[$old]);
2127 }
2128 }
2129 }
2130 }
2131
2132
2133 /**
2134 * Merges two arrays recursively and "binary safe" (integer keys are
2135 * overridden as well), overruling similar values in the first array
2136 * ($arr0) with the values of the second array ($arr1)
2137 * In case of identical keys, ie. keeping the values of the second.
2138 * Usage: 0
2139 *
2140 * @param array First array
2141 * @param array Second array, overruling the first array
2142 * @param boolean If set, keys that are NOT found in $arr0 (first array) will not be set. Thus only existing value can/will be overruled from second array.
2143 * @param boolean If set, values from $arr1 will overrule if they are empty or zero. Default: true
2144 * @return array Resulting array where $arr1 values has overruled $arr0 values
2145 */
2146 public static function array_merge_recursive_overrule(array $arr0, array $arr1, $notAddKeys = 0, $includeEmtpyValues = TRUE) {
2147 foreach ($arr1 as $key => $val) {
2148 if (is_array($arr0[$key])) {
2149 if (is_array($arr1[$key])) {
2150 $arr0[$key] = self::array_merge_recursive_overrule($arr0[$key], $arr1[$key], $notAddKeys, $includeEmtpyValues);
2151 }
2152 } else {
2153 if ($notAddKeys) {
2154 if (isset($arr0[$key])) {
2155 if ($includeEmtpyValues || $val) {
2156 $arr0[$key] = $val;
2157 }
2158 }
2159 } else {
2160 if ($includeEmtpyValues || $val) {
2161 $arr0[$key] = $val;
2162 }
2163 }
2164 }
2165 }
2166 reset($arr0);
2167 return $arr0;
2168 }
2169
2170 /**
2171 * An array_merge function where the keys are NOT renumbered as they happen to be with the real php-array_merge function. It is "binary safe" in the sense that integer keys are overridden as well.
2172 * Usage: 16
2173 *
2174 * @param array First array
2175 * @param array Second array
2176 * @return array Merged result.
2177 */
2178 public static function array_merge(array $arr1, array $arr2) {
2179 return $arr2 + $arr1;
2180 }
2181
2182 /**
2183 * Filters keys off from first array that also exist in second array. Comparision is done by keys.
2184 * This method is a recursive version of php array_diff_assoc()
2185 *
2186 * @param array Source array
2187 * @param array Reduce source array by this array
2188 * @return array Source array reduced by keys also present in second array
2189 */
2190 public static function arrayDiffAssocRecursive(array $array1, array $array2) {
2191 $differenceArray = array();
2192 foreach ($array1 as $key => $value) {
2193 if (!array_key_exists($key, $array2)) {
2194 $differenceArray[$key] = $value;
2195 } elseif (is_array($value)) {
2196 if (is_array($array2[$key])) {
2197 $differenceArray[$key] = self::arrayDiffAssocRecursive($value, $array2[$key]);
2198 }
2199 }
2200 }
2201
2202 return $differenceArray;
2203 }
2204
2205 /**
2206 * Takes a row and returns a CSV string of the values with $delim (default is ,) and $quote (default is ") as separator chars.
2207 * Usage: 5
2208 *
2209 * @param array Input array of values
2210 * @param string Delimited, default is comman
2211 * @param string Quote-character to wrap around the values.
2212 * @return string A single line of CSV
2213 */
2214 public static function csvValues(array $row, $delim = ',', $quote = '"') {
2215 reset($row);
2216 $out = array();
2217 foreach ($row as $value) {
2218 $out[] = str_replace($quote, $quote . $quote, $value);
2219 }
2220 $str = $quote . implode($quote . $delim . $quote, $out) . $quote;
2221 return $str;
2222 }
2223
2224 /**
2225 * Creates recursively a JSON literal from a multidimensional associative array.
2226 * Uses native function of PHP >= 5.2.0
2227 *
2228 * @param array $jsonArray: The array to be transformed to JSON
2229 * @return string JSON string
2230 * @deprecated since TYPO3 4.3, will be removed in TYPO3 4.6 - use PHP native function json_encode() instead, will be removed in TYPO3 4.5
2231 */
2232 public static function array2json(array $jsonArray) {
2233 self::logDeprecatedFunction();
2234
2235 return json_encode($jsonArray);
2236 }
2237
2238 /**
2239 * Removes dots "." from end of a key identifier of TypoScript styled array.
2240 * array('key.' => array('property.' => 'value')) --> array('key' => array('property' => 'value'))
2241 *
2242 * @param array $ts: TypoScript configuration array
2243 * @return array TypoScript configuration array without dots at the end of all keys
2244 */
2245 public static function removeDotsFromTS(array $ts) {
2246 $out = array();
2247 foreach ($ts as $key => $value) {
2248 if (is_array($value)) {
2249 $key = rtrim($key, '.');
2250 $out[$key] = self::removeDotsFromTS($value);
2251 } else {
2252 $out[$key] = $value;
2253 }
2254 }
2255 return $out;
2256 }
2257
2258 /**
2259 * Sorts an array by key recursive - uses natural sort order (aAbB-zZ)
2260 *
2261 * @param array $array array to be sorted recursively, passed by reference
2262 * @return boolean TRUE if param is an array
2263 */
2264 public static function naturalKeySortRecursive(&$array) {
2265 if (!is_array($array)) {
2266 return FALSE;
2267 }
2268 uksort($array, 'strcasecmp');
2269 foreach ($array as $key => $value) {
2270 self::naturalKeySortRecursive($array[$key]);
2271 }
2272 return TRUE;
2273 }
2274
2275
2276 /*************************
2277 *
2278 * HTML/XML PROCESSING
2279 *
2280 *************************/
2281
2282 /**
2283 * Returns an array with all attributes of the input HTML tag as key/value pairs. Attributes are only lowercase a-z
2284 * $tag is either a whole tag (eg '<TAG OPTION ATTRIB=VALUE>') or the parameterlist (ex ' OPTION ATTRIB=VALUE>')
2285 * If an attribute is empty, then the value for the key is empty. You can check if it existed with isset()
2286 * Usage: 8
2287 *
2288 * @param string HTML-tag string (or attributes only)
2289 * @return array Array with the attribute values.
2290 */
2291 public static function get_tag_attributes($tag) {
2292 $components = self::split_tag_attributes($tag);
2293 $name = ''; // attribute name is stored here
2294 $valuemode = FALSE;
2295 $attributes = array();
2296 foreach ($components as $key => $val) {
2297 if ($val != '=') { // Only if $name is set (if there is an attribute, that waits for a value), that valuemode is enabled. This ensures that the attribute is assigned it's value
2298 if ($valuemode) {
2299 if ($name) {
2300 $attributes[$name] = $val;
2301 $name = '';
2302 }
2303 } else {
2304 if ($key = strtolower(preg_replace('/[^a-zA-Z0-9]/', '', $val))) {
2305 $attributes[$key] = '';
2306 $name = $key;
2307 }
2308 }
2309 $valuemode = FALSE;
2310 } else {
2311 $valuemode = TRUE;
2312 }
2313 }
2314 return $attributes;
2315 }
2316
2317 /**
2318 * Returns an array with the 'components' from an attribute list from an HTML tag. The result is normally analyzed by get_tag_attributes
2319 * Removes tag-name if found
2320 * Usage: 2
2321 *
2322 * @param string HTML-tag string (or attributes only)
2323 * @return array Array with the attribute values.
2324 */
2325 public static function split_tag_attributes($tag) {
2326 $tag_tmp = trim(preg_replace('/^<[^[:space:]]*/', '', trim($tag)));
2327 // Removes any > in the end of the string
2328 $tag_tmp = trim(rtrim($tag_tmp, '>'));
2329
2330 $value = array();
2331 while (strcmp($tag_tmp, '')) { // Compared with empty string instead , 030102
2332 $firstChar = substr($tag_tmp, 0, 1);
2333 if (!strcmp($firstChar, '"') || !strcmp($firstChar, "'")) {
2334 $reg = explode($firstChar, $tag_tmp, 3);
2335 $value[] = $reg[1];
2336 $tag_tmp = trim($reg[2]);
2337 } elseif (!strcmp($firstChar, '=')) {
2338 $value[] = '=';
2339 $tag_tmp = trim(substr($tag_tmp, 1)); // Removes = chars.
2340 } else {
2341 // There are '' around the value. We look for the next ' ' or '>'
2342 $reg = preg_split('/[[:space:]=]/', $tag_tmp, 2);
2343 $value[] = trim($reg[0]);
2344 $tag_tmp = trim(substr($tag_tmp, strlen($reg[0]), 1) . $reg[1]);
2345 }
2346 }
2347 reset($value);
2348 return $value;
2349 }
2350
2351 /**
2352 * Implodes attributes in the array $arr for an attribute list in eg. and HTML tag (with quotes)
2353 * Usage: 14
2354 *
2355 * @param array Array with attribute key/value pairs, eg. "bgcolor"=>"red", "border"=>0
2356 * @param boolean If set the resulting attribute list will have a) all attributes in lowercase (and duplicates weeded out, first entry taking precedence) and b) all values htmlspecialchar()'ed. It is recommended to use this switch!
2357 * @param boolean If true, don't check if values are blank. Default is to omit attributes with blank values.
2358 * @return string Imploded attributes, eg. 'bgcolor="red" border="0"'
2359 */
2360 public static function implodeAttributes(array $arr, $xhtmlSafe = FALSE, $dontOmitBlankAttribs = FALSE) {
2361 if ($xhtmlSafe) {
2362 $newArr = array();
2363 foreach ($arr as $p => $v) {
2364 if (!isset($newArr[strtolower($p)])) {
2365 $newArr[strtolower($p)] = htmlspecialchars($v);
2366 }
2367 }
2368 $arr = $newArr;
2369 }
2370 $list = array();
2371 foreach ($arr as $p => $v) {
2372 if (strcmp($v, '') || $dontOmitBlankAttribs) {
2373 $list[] = $p . '="' . $v . '"';
2374 }
2375 }
2376 return implode(' ', $list);
2377 }
2378
2379 /**
2380 * Implodes attributes in the array $arr for an attribute list in eg. and HTML tag (with quotes)
2381 *
2382 * @param array See implodeAttributes()
2383 * @param boolean See implodeAttributes()
2384 * @param boolean See implodeAttributes()
2385 * @return string See implodeAttributes()
2386 * @deprecated since TYPO3 3.7, will be removed in TYPO3 4.6 - Name was changed into implodeAttributes
2387 * @see implodeAttributes()
2388 */
2389 public static function implodeParams(array $arr, $xhtmlSafe = FALSE, $dontOmitBlankAttribs = FALSE) {
2390 self::logDeprecatedFunction();
2391
2392 return self::implodeAttributes($arr, $xhtmlSafe, $dontOmitBlankAttribs);
2393 }
2394
2395 /**
2396 * Wraps JavaScript code XHTML ready with <script>-tags
2397 * Automatic re-identing of the JS code is done by using the first line as ident reference.
2398 * This is nice for identing JS code with PHP code on the same level.
2399 *
2400 * @param string JavaScript code
2401 * @param boolean Wrap script element in linebreaks? Default is TRUE.
2402 * @return string The wrapped JS code, ready to put into a XHTML page
2403 * @author Ingmar Schlecht <ingmars@web.de>
2404 * @author René Fritz <r.fritz@colorcube.de>
2405 */
2406 public static function wrapJS($string, $linebreak = TRUE) {
2407 if (trim($string)) {
2408 // <script wrapped in nl?
2409 $cr = $linebreak ? LF : '';
2410
2411 // remove nl from the beginning
2412 $string = preg_replace('/^\n+/', '', $string);
2413 // re-ident to one tab using the first line as reference
2414 $match = array();
2415 if (preg_match('/^(\t+)/', $string, $match)) {
2416 $string = str_replace($match[1], TAB, $string);
2417 }
2418 $string = $cr . '<script type="text/javascript">
2419 /*<![CDATA[*/
2420 ' . $string . '
2421 /*]]>*/
2422 </script>' . $cr;
2423 }
2424 return trim($string);
2425 }
2426
2427
2428 /**
2429 * Parses XML input into a PHP array with associative keys
2430 * Usage: 0
2431 *
2432 * @param string XML data input
2433 * @param integer Number of element levels to resolve the XML into an array. Any further structure will be set as XML.
2434 * @return mixed The array with the parsed structure unless the XML parser returns with an error in which case the error message string is returned.
2435 * @author bisqwit at iki dot fi dot not dot for dot ads dot invalid / http://dk.php.net/xml_parse_into_struct + kasperYYYY@typo3.com
2436 */
2437 public static function xml2tree($string, $depth = 999) {
2438 $parser = xml_parser_create();
2439 $vals = array();
2440 $index = array();
2441
2442 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2443 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
2444 xml_parse_into_struct($parser, $string, $vals, $index);
2445
2446 if (xml_get_error_code($parser)) {
2447 return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser));
2448 }
2449 xml_parser_free($parser);
2450
2451 $stack = array(array());
2452 $stacktop = 0;
2453 $startPoint = 0;
2454
2455 $tagi = array();
2456 foreach ($vals as $key => $val) {
2457 $type = $val['type'];
2458
2459 // open tag:
2460 if ($type == 'open' || $type == 'complete') {
2461 $stack[$stacktop++] = $tagi;
2462
2463 if ($depth == $stacktop) {
2464 $startPoint = $key;
2465 }
2466
2467 $tagi = array('tag' => $val['tag']);
2468
2469 if (isset($val['attributes'])) {
2470 $tagi['attrs'] = $val['attributes'];
2471 }
2472 if (isset($val['value'])) {
2473 $tagi['values'][] = $val['value'];
2474 }
2475 }
2476 // finish tag:
2477 if ($type == 'complete' || $type == 'close') {
2478 $oldtagi = $tagi;
2479 $tagi = $stack[--$stacktop];
2480 $oldtag = $oldtagi['tag'];
2481 unset($oldtagi['tag']);
2482
2483 if ($depth == ($stacktop + 1)) {
2484 if ($key - $startPoint > 0) {
2485 $partArray = array_slice(
2486 $vals,
2487 $startPoint + 1,
2488 $key - $startPoint - 1
2489 );
2490 #$oldtagi=array('XMLvalue'=>self::xmlRecompileFromStructValArray($partArray));
2491 $oldtagi['XMLvalue'] = self::xmlRecompileFromStructValArray($partArray);
2492 } else {
2493 $oldtagi['XMLvalue'] = $oldtagi['values'][0];
2494 }
2495 }
2496
2497 $tagi['ch'][$oldtag][] = $oldtagi;
2498 unset($oldtagi);
2499 }
2500 // cdata
2501 if ($type == 'cdata') {
2502 $tagi['values'][] = $val['value'];
2503 }
2504 }
2505 return $tagi['ch'];
2506 }
2507
2508 /**
2509 * Turns PHP array into XML. See array2xml()
2510 *
2511 * @param array The input PHP array with any kind of data; text, binary, integers. Not objects though.
2512 * @param string Alternative document tag. Default is "phparray".
2513 * @param array Options for the compilation. See array2xml() for description.
2514 * @param string Forced charset to prologue
2515 * @return string An XML string made from the input content in the array.
2516 * @see xml2array(),array2xml()
2517 */
2518 public static function array2xml_cs(array $array, $docTag = 'phparray', array $options = array(), $charset = '') {
2519
2520 // Figure out charset if not given explicitly:
2521 if (!$charset) {
2522 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset']) { // First priority: forceCharset! If set, this will be authoritative!
2523 $charset = $GLOBALS['TYPO3_CONF_VARS']['BE']['forceCharset'];
2524 } elseif (is_object($GLOBALS['LANG'])) {
2525 $charset = $GLOBALS['LANG']->charSet; // If "LANG" is around, that will hold the current charset
2526 } else {
2527 $charset = 'iso-8859-1'; // THIS is just a hopeful guess!
2528 }
2529 }
2530
2531 // Return XML:
2532 return '<?xml version="1.0" encoding="' . htmlspecialchars($charset) . '" standalone="yes" ?>' . LF .
2533 self::array2xml($array, '', 0, $docTag, 0, $options);
2534 }
2535
2536 /**
2537 * Deprecated to call directly (unless you are aware of using XML prologues)! Use "array2xml_cs" instead (which adds an XML-prologue)
2538 *
2539 * Converts a PHP array into an XML string.
2540 * The XML output is optimized for readability since associative keys are used as tagnames.
2541 * This also means that only alphanumeric characters are allowed in the tag names AND only keys NOT starting with numbers (so watch your usage of keys!). However there are options you can set to avoid this problem.
2542 * Numeric keys are stored with the default tagname "numIndex" but can be overridden to other formats)
2543 * The function handles input values from the PHP array in a binary-safe way; All characters below 32 (except 9,10,13) will trigger the content to be converted to a base64-string
2544 * The PHP variable type of the data IS preserved as long as the types are strings, arrays, integers and booleans. Strings are the default type unless the "type" attribute is set.
2545 * The output XML has been tested with the PHP XML-parser and parses OK under all tested circumstances with 4.x versions. However, with PHP5 there seems to be the need to add an XML prologue a la <?xml version="1.0" encoding="[charset]" standalone="yes" ?> - otherwise UTF-8 is assumed! Unfortunately, many times the output from this function is used without adding that prologue meaning that non-ASCII characters will break the parsing!! This suchs of course! Effectively it means that the prologue should always be prepended setting the right characterset, alternatively the system should always run as utf-8!
2546 * However using MSIE to read the XML output didn't always go well: One reason could be that the character encoding is not observed in the PHP data. The other reason may be if the tag-names are invalid in the eyes of MSIE. Also using the namespace feature will make MSIE break parsing. There might be more reasons...
2547 * Usage: 5
2548 *
2549 * @param array The input PHP array with any kind of data; text, binary, integers. Not objects though.
2550 * @param string tag-prefix, eg. a namespace prefix like "T3:"
2551 * @param integer Current recursion level. Don't change, stay at zero!
2552 * @param string Alternative document tag. Default is "phparray".
2553 * @param integer If greater than zero, then the number of spaces corresponding to this number is used for indenting, if less than zero - no indentation, if zero - a single TAB is used
2554 * @param array Options for the compilation. Key "useNindex" => 0/1 (boolean: whether to use "n0, n1, n2" for num. indexes); Key "useIndexTagForNum" => "[tag for numerical indexes]"; Key "useIndexTagForAssoc" => "[tag for associative indexes"; Key "parentTagMap" => array('parentTag' => 'thisLevelTag')
2555 * @param string Stack data. Don't touch.
2556 * @return string An XML string made from the input content in the array.
2557 * @see xml2array()
2558 */
2559 public static function array2xml(array $array, $NSprefix = '', $level = 0, $docTag = 'phparray', $spaceInd = 0, array $options = array(), array $stackData = array()) {
2560 // The list of byte values which will trigger binary-safe storage. If any value has one of these char values in it, it will be encoded in base64
2561 $binaryChars = chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8) .
2562 chr(11) . chr(12) . chr(14) . chr(15) . chr(16) . chr(17) . chr(18) . chr(19) .
2563 chr(20) . chr(21) . chr(22) . chr(23) . chr(24) . chr(25) . chr(26) . chr(27) . chr(28) . chr(29) .
2564 chr(30) . chr(31);
2565 // Set indenting mode:
2566 $indentChar = $spaceInd ? ' ' : TAB;
2567 $indentN = $spaceInd > 0 ? $spaceInd : 1;
2568 $nl = ($spaceInd >= 0 ? LF : '');
2569
2570 // Init output variable:
2571 $output = '';
2572
2573 // Traverse the input array
2574 foreach ($array as $k => $v) {
2575 $attr = '';
2576 $tagName = $k;
2577
2578 // Construct the tag name.
2579 if (isset($options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']])) { // Use tag based on grand-parent + parent tag name
2580 $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2581 $tagName = (string) $options['grandParentTagMap'][$stackData['grandParentTagName'] . '/' . $stackData['parentTagName']];
2582 } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM']) && self::testInt($tagName)) { // Use tag based on parent tag name + if current tag is numeric
2583 $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2584 $tagName = (string) $options['parentTagMap'][$stackData['parentTagName'] . ':_IS_NUM'];
2585 } elseif (isset($options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName])) { // Use tag based on parent tag name + current tag
2586 $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2587 $tagName = (string) $options['parentTagMap'][$stackData['parentTagName'] . ':' . $tagName];
2588 } elseif (isset($options['parentTagMap'][$stackData['parentTagName']])) { // Use tag based on parent tag name:
2589 $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2590 $tagName = (string) $options['parentTagMap'][$stackData['parentTagName']];
2591 } elseif (!strcmp(intval($tagName), $tagName)) { // If integer...;
2592 if ($options['useNindex']) { // If numeric key, prefix "n"
2593 $tagName = 'n' . $tagName;
2594 } else { // Use special tag for num. keys:
2595 $attr .= ' index="' . $tagName . '"';
2596 $tagName = $options['useIndexTagForNum'] ? $options['useIndexTagForNum'] : 'numIndex';
2597 }
2598 } elseif ($options['useIndexTagForAssoc']) { // Use tag for all associative keys:
2599 $attr .= ' index="' . htmlspecialchars($tagName) . '"';
2600 $tagName = $options['useIndexTagForAssoc'];
2601 }
2602
2603 // The tag name is cleaned up so only alphanumeric chars (plus - and _) are in there and not longer than 100 chars either.
2604 $tagName = substr(preg_replace('/[^[:alnum:]_-]/', '', $tagName), 0, 100);
2605
2606 // If the value is an array then we will call this function recursively:
2607 if (is_array($v)) {
2608
2609 // Sub elements:
2610 if ($options['alt_options'][$stackData['path'] . '/' . $tagName]) {
2611 $subOptions = $options['alt_options'][$stackData['path'] . '/' . $tagName];
2612 $clearStackPath = $subOptions['clearStackPath'];
2613 } else {
2614 $subOptions = $options;
2615 $clearStackPath = FALSE;
2616 }
2617
2618 $content = $nl .
2619 self::array2xml(
2620 $v,
2621 $NSprefix,
2622 $level + 1,
2623 '',
2624 $spaceInd,
2625 $subOptions,
2626 array(
2627 'parentTagName' => $tagName,
2628 'grandParentTagName' => $stackData['parentTagName'],
2629 'path' => $clearStackPath ? '' : $stackData['path'] . '/' . $tagName,
2630 )
2631 ) .
2632 ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '');
2633 if ((int) $options['disableTypeAttrib'] != 2) { // Do not set "type = array". Makes prettier XML but means that empty arrays are not restored with xml2array
2634 $attr .= ' type="array"';
2635 }
2636 } else { // Just a value:
2637
2638 // Look for binary chars:
2639 $vLen = strlen($v); // check for length, because PHP 5.2.0 may crash when first argument of strcspn is empty
2640 if ($vLen && strcspn($v, $binaryChars) != $vLen) { // Go for base64 encoding if the initial segment NOT matching any binary char has the same length as the whole string!
2641 // If the value contained binary chars then we base64-encode it an set an attribute to notify this situation:
2642 $content = $nl . chunk_split(base64_encode($v));
2643 $attr .= ' base64="1"';
2644 } else {
2645 // Otherwise, just htmlspecialchar the stuff:
2646 $content = htmlspecialchars($v);
2647 $dType = gettype($v);
2648 if ($dType == 'string') {
2649 if ($options['useCDATA'] && $content != $v) {
2650 $content = '<![CDATA[' . $v . ']]>';
2651 }
2652 } elseif (!$options['disableTypeAttrib']) {
2653 $attr .= ' type="' . $dType . '"';
2654 }
2655 }
2656 }
2657
2658 // Add the element to the output string:
2659 $output .= ($spaceInd >= 0 ? str_pad('', ($level + 1) * $indentN, $indentChar) : '') . '<' . $NSprefix . $tagName . $attr . '>' . $content . '</' . $NSprefix . $tagName . '>' . $nl;
2660 }
2661
2662 // If we are at the outer-most level, then we finally wrap it all in the document tags and return that as the value:
2663 if (!$level) {
2664 $output =
2665 '<' . $docTag . '>' . $nl .
2666 $output .
2667 '</' . $docTag . '>';
2668 }
2669
2670 return $output;
2671 }
2672
2673 /**
2674 * Converts an XML string to a PHP array.
2675 * This is the reverse function of array2xml()
2676 * This is a wrapper for xml2arrayProcess that adds a two-level cache
2677 * Usage: 17
2678 *
2679 * @param string XML content to convert into an array
2680 * @param string The tag-prefix resolve, eg. a namespace like "T3:"
2681 * @param boolean If set, the document tag will be set in the key "_DOCUMENT_TAG" of the output array
2682 * @return mixed If the parsing had errors, a string with the error message is returned. Otherwise an array with the content.
2683 * @see array2xml(),xml2arrayProcess()
2684 * @author Fabrizio Branca <typo3@fabrizio-branca.de> (added caching)
2685 */
2686 public static function xml2array($string, $NSprefix = '', $reportDocTag = FALSE) {
2687 static $firstLevelCache = array();
2688
2689 $identifier = md5($string . $NSprefix . ($reportDocTag ? '1' : '0'));
2690
2691 // look up in first level cache
2692 if (!empty($firstLevelCache[$identifier])) {
2693 $array = $firstLevelCache[$identifier];
2694 } else {
2695 // look up in second level cache
2696 $cacheContent = t3lib_pageSelect::getHash($identifier, 0);
2697 $array = unserialize($cacheContent);
2698
2699 if ($array === FALSE) {
2700 $array = self::xml2arrayProcess($string, $NSprefix, $reportDocTag);
2701 t3lib_pageSelect::storeHash($identifier, serialize($array), 'ident_xml2array');
2702 }
2703 // store content in first level cache
2704 $firstLevelCache[$identifier] = $array;
2705 }
2706 return $array;
2707 }
2708
2709 /**
2710 * Converts an XML string to a PHP array.
2711 * This is the reverse function of array2xml()
2712 * Usage: 1
2713 *
2714 * @param string XML content to convert into an array
2715 * @param string The tag-prefix resolve, eg. a namespace like "T3:"
2716 * @param boolean If set, the document tag will be set in the key "_DOCUMENT_TAG" of the output array
2717 * @return mixed If the parsing had errors, a string with the error message is returned. Otherwise an array with the content.
2718 * @see array2xml()
2719 */
2720 protected function xml2arrayProcess($string, $NSprefix = '', $reportDocTag = FALSE) {
2721 global $TYPO3_CONF_VARS;
2722
2723 // Create parser:
2724 $parser = xml_parser_create();
2725 $vals = array();
2726 $index = array();
2727
2728 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
2729 xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 0);
2730
2731 // default output charset is UTF-8, only ASCII, ISO-8859-1 and UTF-8 are supported!!!
2732 $match = array();
2733 preg_match('/^[[:space:]]*<\?xml[^>]*encoding[[:space:]]*=[[:space:]]*"([^"]*)"/', substr($string, 0, 200), $match);
2734 $theCharset = $match[1] ? $match[1] : ($TYPO3_CONF_VARS['BE']['forceCharset'] ? $TYPO3_CONF_VARS['BE']['forceCharset'] : 'iso-8859-1');
2735 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $theCharset); // us-ascii / utf-8 / iso-8859-1
2736
2737 // Parse content:
2738 xml_parse_into_struct($parser, $string, $vals, $index);
2739
2740 // If error, return error message:
2741 if (xml_get_error_code($parser)) {
2742 return 'Line ' . xml_get_current_line_number($parser) . ': ' . xml_error_string(xml_get_error_code($parser));
2743 }
2744 xml_parser_free($parser);
2745
2746 // Init vars:
2747 $stack = array(array());
2748 $stacktop = 0;
2749 $current = array();
2750 $tagName = '';
2751 $documentTag = '';
2752
2753 // Traverse the parsed XML structure:
2754 foreach ($vals as $key => $val) {
2755
2756 // First, process the tag-name (which is used in both cases, whether "complete" or "close")
2757 $tagName = $val['tag'];
2758 if (!$documentTag) {
2759 $documentTag = $tagName;
2760 }
2761
2762 // Test for name space:
2763 $tagName = ($NSprefix && substr($tagName, 0, strlen($NSprefix)) == $NSprefix) ? substr($tagName, strlen($NSprefix)) : $tagName;
2764
2765 // Test for numeric tag, encoded on the form "nXXX":
2766 $testNtag = substr($tagName, 1); // Closing tag.
2767 $tagName = (substr($tagName, 0, 1) == 'n' && !strcmp(intval($testNtag), $testNtag)) ? intval($testNtag) : $tagName;
2768
2769 // Test for alternative index value:
2770 if (strlen($val['attributes']['index'])) {
2771 $tagName = $val['attributes']['index'];
2772 }
2773
2774 // Setting tag-values, manage stack:
2775 switch ($val['type']) {
2776 case 'open': // If open tag it means there is an array stored in sub-elements. Therefore increase the stackpointer and reset the accumulation array:
2777 $current[$tagName] = array(); // Setting blank place holder
2778 $stack[$stacktop++] = $current;
2779 $current = array();
2780 break;
2781 case 'close': // If the tag is "close" then it is an array which is closing and we decrease the stack pointer.
2782 $oldCurrent = $current;
2783 $current = $stack[--$stacktop];
2784 end($current); // Going to the end of array to get placeholder key, key($current), and fill in array next:
2785 $current[key($current)] = $oldCurrent;
2786 unset($oldCurrent);
2787 break;
2788 case 'complete': // If "complete", then it's a value. If the attribute "base64" is set, then decode the value, otherwise just set it.
2789 if ($val['attributes']['base64']) {
2790 $current[$tagName] = base64_decode($val['value']);
2791 } else {
2792 $current[$tagName] = (string) $val['value']; // Had to cast it as a string - otherwise it would be evaluate false if tested with isset()!!
2793
2794 // Cast type:
2795 switch ((string) $val['attributes']['type']) {
2796 case 'integer':
2797 $current[$tagName] = (integer) $current[$tagName];
2798 break;
2799 case 'double':
2800 $current[$tagName] = (double) $current[$tagName];
2801 break;
2802 case 'boolean':
2803 $current[$tagName] = (bool) $current[$tagName];
2804 break;
2805 case 'array':
2806 $current[$tagName] = array(); // MUST be an empty array since it is processed as a value; Empty arrays would end up here because they would have no tags inside...
2807 break;
2808 }
2809 }
2810 break;
2811 }
2812 }
2813
2814 if ($reportDocTag) {
2815 $current[$tagName]['_DOCUMENT_TAG'] = $documentTag;
2816 }
2817
2818 // Finally return the content of the document tag.
2819 return $current[$tagName];
2820 }
2821
2822 /**
2823 * This implodes an array of XML parts (made with xml_parse_into_struct()) into XML again.
2824 * Usage: 2
2825 *
2826 * @param array A array of XML parts, see xml2tree
2827 * @return string Re-compiled XML data.
2828 */
2829 public static function xmlRecompileFromStructValArray(array $vals) {
2830 $XMLcontent = '';
2831
2832 foreach ($vals as $val) {
2833 $type = $val['type'];
2834
2835 // open tag:
2836 if ($type == 'open' || $type == 'complete') {
2837 $XMLcontent .= '<' . $val['tag'];
2838 if (isset($val['attributes'])) {
2839 foreach ($val['attributes'] as $k => $v) {
2840 $XMLcontent .= ' ' . $k . '="' . htmlspecialchars($v) . '"';
2841 }
2842 }
2843 if ($type == 'complete') {
2844 if (isset($val['value'])) {
2845 $XMLcontent .= '>' . htmlspecialchars($val['value']) . '</' . $val['tag'] . '>';
2846 } else {
2847 $XMLcontent .= '/>';
2848 }
2849 } else {
2850 $XMLcontent .= '>';
2851 }
2852
2853 if ($type == 'open' && isset($val['value'])) {
2854 $XMLcontent .= htmlspecialchars($val['value']);
2855 }
2856 }
2857 // finish tag:
2858 if ($type == 'close') {
2859 $XMLcontent .= '</' . $val['tag'] . '>';
2860 }
2861 // cdata
2862 if ($type == 'cdata') {
2863 $XMLcontent .= htmlspecialchars($val['value']);
2864 }
2865 }
2866
2867 return $XMLcontent;
2868 }
2869
2870 /**
2871 * Extracts the attributes (typically encoding and version) of an XML prologue (header).
2872 * Usage: 1
2873 *
2874 * @param string XML data
2875 * @return array Attributes of the xml prologue (header)
2876 */
2877 public static function xmlGetHeaderAttribs($xmlData) {
2878 $match = array();
2879 if (preg_match('/^\s*<\?xml([^>]*)\?\>/', $xmlData, $match)) {
2880 return self::get_tag_attributes($match[1]);
2881 }
2882 }
2883
2884 /**
2885 * Minifies JavaScript
2886 *
2887 * @param string $script Script to minify
2888 * @param string $error Error message (if any)
2889 * @return string Minified script or source string if error happened
2890 */
2891 public static function minifyJavaScript($script, &$error = '') {
2892 require_once(PATH_typo3 . 'contrib/jsmin/jsmin.php');
2893 try {
2894 $error = '';
2895 $script = trim(JSMin::minify(str_replace(CR, '', $script)));
2896 }
2897 catch (JSMinException $e) {
2898 $error = 'Error while minifying JavaScript: ' . $e->getMessage();
2899 self::devLog($error, 't3lib_div', 2,
2900 array('JavaScript' => $script, 'Stack trace' => $e->getTrace()));
2901 }
2902 return $script;
2903 }
2904
2905
2906 /*************************
2907 *
2908 * FILES FUNCTIONS
2909 *
2910 *************************/
2911
2912 /**
2913 * Reads the file or url $url and returns the content
2914 * If you are having trouble with proxys when reading URLs you can configure your way out of that with settings like $TYPO3_CONF_VARS['SYS']['curlUse'] etc.
2915 * Usage: 83
2916 *
2917 * @param string File/URL to read
2918 * @param integer Whether the HTTP header should be fetched or not. 0=disable, 1=fetch header+content, 2=fetch header only
2919 * @param array HTTP headers to be used in the request
2920 * @param array Error code/message and, if $includeHeader is 1, response meta data (HTTP status and content type)
2921 * @return string The content from the resource given as input. FALSE if an error has occured.
2922 */
2923 public static function getURL($url, $includeHeader = 0, $requestHeaders = FALSE, &$report = NULL) {
2924 $content = FALSE;
2925
2926 if (isset($report)) {
2927 $report['error'] = 0;
2928 $report['message'] = '';
2929 }
2930
2931 // use cURL for: http, https, ftp, ftps, sftp and scp
2932 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse'] == '1' && preg_match('/^(?:http|ftp)s?|s(?:ftp|cp):/', $url)) {
2933 if (isset($report)) {
2934 $report['lib'] = 'cURL';
2935 }
2936
2937 // External URL without error checking.
2938 $ch = curl_init();
2939 if (!$ch) {
2940 if (isset($report)) {
2941 $report['error'] = -1;
2942 $report['message'] = 'Couldn\'t initialize cURL.';
2943 }
2944 return FALSE;
2945 }
2946
2947 curl_setopt($ch, CURLOPT_URL, $url);
2948 curl_setopt($ch, CURLOPT_HEADER, $includeHeader ? 1 : 0);
2949 curl_setopt($ch, CURLOPT_NOBODY, $includeHeader == 2 ? 1 : 0);
2950 curl_setopt($ch, CURLOPT_HTTPGET, $includeHeader == 2 ? 'HEAD' : 'GET');
2951 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
2952 curl_setopt($ch, CURLOPT_FAILONERROR, 1);
2953 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, max(0, intval($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlTimeout'])));
2954
2955 // may fail (PHP 5.2.0+ and 5.1.5+) when open_basedir or safe_mode are enabled
2956 $followLocation = @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
2957
2958 if (is_array($requestHeaders)) {
2959 curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
2960 }
2961
2962 // (Proxy support implemented by Arco <arco@appeltaart.mine.nu>)
2963 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']) {
2964 curl_setopt($ch, CURLOPT_PROXY, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyServer']);
2965
2966 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']) {
2967 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyTunnel']);
2968 }
2969 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']) {
2970 curl_setopt($ch, CURLOPT_PROXYUSERPWD, $GLOBALS['TYPO3_CONF_VARS']['SYS']['curlProxyUserPass']);
2971 }
2972 }
2973 $content = curl_exec($ch);
2974 if (isset($report)) {
2975 if ($content === FALSE) {
2976 $report['error'] = curl_errno($ch);
2977 $report['message'] = curl_error($ch);
2978 } else {
2979 $curlInfo = curl_getinfo($ch);
2980 // We hit a redirection but we couldn't follow it
2981 if (!$followLocation && $curlInfo['status'] >= 300 && $curlInfo['status'] < 400) {
2982 $report['error'] = -1;
2983 $report['message'] = 'Couldn\'t follow location redirect (either PHP configuration option safe_mode or open_basedir is in effect).';
2984 } elseif ($includeHeader) {
2985 // Set only for $includeHeader to work exactly like PHP variant
2986 $report['http_code'] = $curlInfo['http_code'];
2987 $report['content_type'] = $curlInfo['content_type'];
2988 }
2989 }
2990 }
2991 curl_close($ch);
2992
2993 } elseif ($includeHeader) {
2994 if (isset($report)) {
2995 $report['lib'] = 'socket';
2996 }
2997 $parsedURL = parse_url($url);
2998 if (!preg_match('/^https?/', $parsedURL['scheme'])) {
2999 if (isset($report)) {
3000 $report['error'] = -1;
3001 $report['message'] = 'Reading headers is not allowed for this protocol.';
3002 }
3003 return FALSE;
3004 }
3005 $port = intval($parsedURL['port']);
3006 if ($port < 1) {
3007 if ($parsedURL['scheme'] == 'http') {
3008 $port = ($port > 0 ? $port : 80);
3009 $scheme = '';
3010 } else {
3011 $port = ($port > 0 ? $port : 443);
3012 $scheme = 'ssl://';
3013 }
3014 }
3015 $errno = 0;
3016 // $errstr = '';
3017 $fp = @fsockopen($scheme . $parsedURL['host'], $port, $errno, $errstr, 2.0);
3018 if (!$fp || $errno > 0) {
3019 if (isset($report)) {
3020 $report['error'] = $errno ? $errno : -1;
3021 $report['message'] = $errno ? ($errstr ? $errstr : 'Socket error.') : 'Socket initialization error.';
3022 }
3023 return FALSE;
3024 }
3025 $method = ($includeHeader == 2) ? 'HEAD' : 'GET';
3026 $msg = $method . ' ' . $parsedURL['path'] .
3027 ($parsedURL['query'] ? '?' . $parsedURL['query'] : '') .
3028 ' HTTP/1.0' . CRLF . 'Host: ' .
3029 $parsedURL['host'] . "\r\nConnection: close\r\n";
3030 if (is_array($requestHeaders)) {
3031 $msg .= implode(CRLF, $requestHeaders) . CRLF;
3032 }
3033 $msg .= CRLF;
3034
3035 fputs($fp, $msg);
3036 while (!feof($fp)) {
3037 $line = fgets($fp, 2048);
3038 if (isset($report)) {
3039 if (preg_match('|^HTTP/\d\.\d +(\d+)|', $line, $status)) {
3040 $report['http_code'] = $status[1];
3041 }
3042 elseif (preg_match('/^Content-Type: *(.*)/i', $line, $type)) {
3043 $report['content_type'] = $type[1];
3044 }
3045 }
3046 $content .= $line;
3047 if (!strlen(trim($line))) {
3048 break; // Stop at the first empty line (= end of header)
3049 }
3050 }
3051 if ($includeHeader != 2) {
3052 $content .= stream_get_contents($fp);
3053 }
3054 fclose($fp);
3055
3056 } elseif (is_array($requestHeaders)) {
3057 if (isset($report)) {
3058 $report['lib'] = 'file/context';
3059 }
3060 $parsedURL = parse_url($url);
3061 if (!preg_match('/^https?/', $parsedURL['scheme'])) {
3062 if (isset($report)) {
3063 $report['error'] = -1;
3064 $report['message'] = 'Sending request headers is not allowed for this protocol.';
3065 }
3066 return FALSE;
3067 }
3068 $ctx = stream_context_create(array(
3069 'http' => array(
3070 'header' => implode(CRLF, $requestHeaders)
3071 )
3072 )
3073 );
3074 $content = @file_get_contents($url, FALSE, $ctx);
3075 if ($content === FALSE && isset($report)) {
3076 $phpError = error_get_last();
3077 $report['error'] = $phpError['type'];
3078 $report['message'] = $phpError['message'];
3079 }
3080 } else {
3081 if (isset($report)) {
3082 $report['lib'] = 'file';
3083 }
3084 $content = @file_get_contents($url);
3085 if ($content === FALSE && isset($report)) {
3086 if (function_exists('error_get_last')) {
3087 $phpError = error_get_last();
3088 $report['error'] = $phpError['type'];
3089 $report['message'] = $phpError['message'];
3090 } else {
3091 $report['error'] = -1;
3092 $report['message'] = 'Couldn\'t get URL.';
3093 }
3094 }
3095 }
3096
3097 return $content;
3098 }
3099
3100 /**
3101 * Writes $content to the file $file
3102 * Usage: 30
3103 *
3104 * @param string Filepath to write to
3105 * @param string Content to write
3106 * @return boolean True if the file was successfully opened and written to.
3107 */
3108 public static function writeFile($file, $content) {
3109 if (!@is_file($file)) {
3110 $changePermissions = TRUE;
3111 }
3112
3113 if ($fd = fopen($file, 'wb')) {
3114 $res = fwrite($fd, $content);
3115 fclose($fd);
3116
3117 if ($res === FALSE) {
3118 return FALSE;
3119 }
3120
3121 if ($changePermissions) { // Change the permissions only if the file has just been created
3122 self::fixPermissions($file);
3123 }
3124
3125 return TRUE;
3126 }
3127
3128 return FALSE;
3129 }
3130
3131 /**
3132 * Sets the file system mode and group ownership of a file or a folder.
3133 *
3134 * @param string Absolute filepath of file or folder, must not be escaped.
3135 * @param boolean If set, also fixes permissions of files and folders in the folder (if $path is a folder)
3136 * @return mixed TRUE on success, FALSE on error, always TRUE on Windows OS
3137 */
3138 public static function fixPermissions($path, $recursive = FALSE) {
3139 if (TYPO3_OS != 'WIN') {
3140 $result = FALSE;
3141 if (self::isAllowedAbsPath($path)) {
3142 if (@is_file($path)) {
3143 // "@" is there because file is not necessarily OWNED by the user
3144 $result = @chmod($path, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['fileCreateMask']));
3145 } elseif (@is_dir($path)) {
3146 $path = preg_replace('|/$|', '', $path);
3147 // "@" is there because file is not necessarily OWNED by the user
3148 $result = @chmod($path, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask']));
3149 }
3150
3151 // Set createGroup if not empty
3152 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']) {
3153 // "@" is there because file is not necessarily OWNED by the user
3154 $changeGroupResult = @chgrp($path, $GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']);
3155 $result = $changeGroupResult ? $result : FALSE;
3156 }
3157
3158 // Call recursive if recursive flag if set and $path is directory
3159 if ($recursive && @is_dir($path)) {
3160 $handle = opendir($path);
3161 while (($file = readdir($handle)) !== FALSE) {
3162 $recursionResult = NULL;
3163 if ($file !== '.' && $file !== '..') {
3164 if (@is_file($path . '/' . $file)) {
3165 $recursionResult = self::fixPermissions($path . '/' . $file);
3166 } elseif (@is_dir($path . '/' . $file)) {
3167 $recursionResult = self::fixPermissions($path . '/' . $file, TRUE);
3168 }
3169 if (isset($recursionResult) && !$recursionResult) {
3170 $result = FALSE;
3171 }
3172 }
3173 }
3174 closedir($handle);
3175 }
3176 }
3177 } else {
3178 $result = TRUE;
3179 }
3180 return $result;
3181 }
3182
3183 /**
3184 * Writes $content to a filename in the typo3temp/ folder (and possibly one or two subfolders...)
3185 * Accepts an additional subdirectory in the file path!
3186 *
3187 * @param string Absolute filepath to write to inside "typo3temp/". First part of this string must match PATH_site."typo3temp/"
3188 * @param string Content string to write
3189 * @return string Returns false on success, otherwise an error string telling about the problem.
3190 */
3191 public static function writeFileToTypo3tempDir($filepath, $content) {
3192
3193 // Parse filepath into directory and basename:
3194 $fI = pathinfo($filepath);
3195 $fI['dirname'] .= '/';
3196
3197 // Check parts:
3198 if (self::validPathStr($filepath) && $fI['basename'] && strlen($fI['basename']) < 60) {
3199 if (defined('PATH_site')) {
3200 $dirName = PATH_site . 'typo3temp/'; // Setting main temporary directory name (standard)
3201 if (@is_dir($dirName)) {
3202 if (self::isFirstPartOfStr($fI['dirname'], $dirName)) {
3203
3204 // Checking if the "subdir" is found:
3205 $subdir = substr($fI['dirname'], strlen($dirName));
3206 if ($subdir) {
3207 if (preg_match('/^[[:alnum:]_]+\/$/', $subdir) || preg_match('/^[[:alnum:]_]+\/[[:alnum:]_]+\/$/', $subdir)) {
3208 $dirName .= $subdir;
3209 if (!@is_dir($dirName)) {
3210 self::mkdir_deep(PATH_site . 'typo3temp/', $subdir);
3211 }
3212 } else {
3213 return 'Subdir, "' . $subdir . '", was NOT on the form "[[:alnum:]_]/" or "[[:alnum:]_]/[[:alnum:]_]/"';
3214 }
3215 }
3216 // Checking dir-name again (sub-dir might have been created):
3217 if (@is_dir($dirName)) {
3218 if ($filepath == $dirName . $fI['basename']) {
3219 self::writeFile($filepath, $content);
3220 if (!@is_file($filepath)) {
3221 return 'File not written to disk! Write permission error in filesystem?';
3222 }
3223 } else {
3224 return 'Calculated filelocation didn\'t match input $filepath!';
3225 }
3226 } else {
3227 return '"' . $dirName . '" is not a directory!';
3228 }
3229 } else {
3230 return '"' . $fI['dirname'] . '" was not within directory PATH_site + "typo3temp/"';
3231 }
3232 } else {
3233 return 'PATH_site + "typo3temp/" was not a directory!';
3234 }
3235 } else {
3236 return 'PATH_site constant was NOT defined!';
3237 }
3238 } else {
3239 return 'Input filepath "' . $filepath . '" was generally invalid!';
3240 }
3241 }
3242
3243 /**
3244 * Wrapper function for mkdir.
3245 * Sets folder permissions according to $GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask']
3246 * and group ownership according to $GLOBALS['TYPO3_CONF_VARS']['BE']['createGroup']
3247 *
3248 * @param string Absolute path to folder, see PHP mkdir() function. Removes trailing slash internally.
3249 * @return boolean TRUE if @mkdir went well!
3250 */
3251 public static function mkdir($newFolder) {
3252 $newFolder = preg_replace('|/$|', '', $newFolder);
3253 $result = @mkdir($newFolder, octdec($GLOBALS['TYPO3_CONF_VARS']['BE']['folderCreateMask']));
3254 if ($result) {
3255 self::fixPermissions($newFolder);
3256 }
3257 return $result;
3258 }
3259
3260 /**
3261 * Creates a directory - including parent directories if necessary - in the file system
3262 *
3263 * @param string Base folder. This must exist! Must have trailing slash! Example "/root/typo3site/"
3264 * @param string Deep directory to create, eg. "xx/yy/" which creates "/root/typo3site/xx/yy/" if $destination is "/root/typo3site/"
3265 * @return string If error, returns error string.
3266 */
3267 public static function mkdir_deep($destination, $deepDir) {
3268 $allParts = self::trimExplode('/', $deepDir, 1);
3269 $root = '';
3270 foreach ($allParts as $part) {
3271