Util.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. <?php
  2. /**
  3. * The Horde_Util:: class provides generally useful methods.
  4. *
  5. * Copyright 1999-2013 Horde LLC (http://www.horde.org/)
  6. *
  7. * See the enclosed file COPYING for license information (LGPL). If you
  8. * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  9. *
  10. * @author Chuck Hagenbuch <chuck@horde.org>
  11. * @author Jon Parise <jon@horde.org>
  12. * @category Horde
  13. * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
  14. * @package Util
  15. */
  16. class Horde_Util
  17. {
  18. /**
  19. * A list of random patterns to use for overwriting purposes.
  20. * See http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html.
  21. * We save the random overwrites for efficiency reasons.
  22. *
  23. * @var array
  24. */
  25. static public $patterns = array(
  26. "\x55", "\xaa", "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49",
  27. "\x00", "\x11", "\x22", "\x33", "\x44", "\x55", "\x66", "\x77",
  28. "\x88", "\x99", "\xaa", "\xbb", "\xcc", "\xdd", "\xee", "\xff",
  29. "\x92\x49\x24", "\x49\x24\x92", "\x24\x92\x49", "\x6d\xb6\xdb",
  30. "\xb6\xdb\x6d", "\xdb\x6d\xb6"
  31. );
  32. /**
  33. * Are magic quotes in use?
  34. *
  35. * @var boolean
  36. */
  37. static protected $_magicquotes = null;
  38. /**
  39. * TODO
  40. *
  41. * @var array
  42. */
  43. static protected $_shutdowndata = array(
  44. 'dirs' => array(),
  45. 'files' => array(),
  46. 'secure' => array()
  47. );
  48. /**
  49. * Has the shutdown method been registered?
  50. *
  51. * @var boolean
  52. */
  53. static protected $_shutdownreg = false;
  54. /**
  55. * Cache for extensionExists().
  56. *
  57. * @var array
  58. */
  59. static protected $_cache = array();
  60. /**
  61. * Checks to see if a value has been set by the script and not by GET,
  62. * POST, or cookie input. The value being checked MUST be in the global
  63. * scope.
  64. *
  65. * @param string $varname The variable name to check.
  66. * @param mixed $default Default value if the variable isn't present
  67. * or was specified by the user. Defaults to null.
  68. *
  69. * @return mixed $default if the var is in user input or not present,
  70. * the variable value otherwise.
  71. */
  72. static public function nonInputVar($varname, $default = null)
  73. {
  74. return (isset($_GET[$varname]) || isset($_POST[$varname]) || isset($_COOKIE[$varname]))
  75. ? $default
  76. : (isset($GLOBALS[$varname]) ? $GLOBALS[$varname] : $default);
  77. }
  78. /**
  79. * Returns a hidden form input containing the session name and id.
  80. *
  81. * @param boolean $append_session 0 = only if needed, 1 = always.
  82. *
  83. * @return string The hidden form input, if needed/requested.
  84. */
  85. static public function formInput($append_session = 0)
  86. {
  87. return (($append_session == 1) || !isset($_COOKIE[session_name()]))
  88. ? '<input type="hidden" name="' . htmlspecialchars(session_name()) . '" value="' . htmlspecialchars(session_id()) . "\" />\n"
  89. : '';
  90. }
  91. /**
  92. * Prints a hidden form input containing the session name and id.
  93. *
  94. * @param boolean $append_session 0 = only if needed, 1 = always.
  95. */
  96. static public function pformInput($append_session = 0)
  97. {
  98. echo self::formInput($append_session);
  99. }
  100. /**
  101. * If magic_quotes_gpc is in use, run stripslashes() on $var.
  102. *
  103. * @param mixed $var The string, or an array of strings, to un-quote.
  104. *
  105. * @return mixed $var, minus any magic quotes.
  106. */
  107. static public function dispelMagicQuotes($var)
  108. {
  109. if (is_null(self::$_magicquotes)) {
  110. self::$_magicquotes = get_magic_quotes_gpc();
  111. }
  112. if (self::$_magicquotes) {
  113. $var = is_array($var)
  114. ? array_map(array(__CLASS__, 'dispelMagicQuotes'), $var)
  115. : stripslashes($var);
  116. }
  117. return $var;
  118. }
  119. /**
  120. * Gets a form variable from GET or POST data, stripped of magic quotes if
  121. * necessary. If the variable is somehow set in both the GET data and the
  122. * POST data, the value from the POST data will be returned and the GET
  123. * value will be ignored.
  124. *
  125. * @param string $var The name of the form variable to look for.
  126. * @param string $default The value to return if the variable is not
  127. * there.
  128. *
  129. * @return string The cleaned form variable, or $default.
  130. */
  131. static public function getFormData($var, $default = null)
  132. {
  133. return (($val = self::getPost($var)) !== null)
  134. ? $val
  135. : self::getGet($var, $default);
  136. }
  137. /**
  138. * Gets a form variable from GET data, stripped of magic quotes if
  139. * necessary. This function will NOT return a POST variable.
  140. *
  141. * @param string $var The name of the form variable to look for.
  142. * @param string $default The value to return if the variable is not
  143. * there.
  144. *
  145. * @return string The cleaned form variable, or $default.
  146. */
  147. static public function getGet($var, $default = null)
  148. {
  149. return (isset($_GET[$var]))
  150. ? self::dispelMagicQuotes($_GET[$var])
  151. : $default;
  152. }
  153. /**
  154. * Gets a form variable from POST data, stripped of magic quotes if
  155. * necessary. This function will NOT return a GET variable.
  156. *
  157. * @param string $var The name of the form variable to look for.
  158. * @param string $default The value to return if the variable is not
  159. * there.
  160. *
  161. * @return string The cleaned form variable, or $default.
  162. */
  163. static public function getPost($var, $default = null)
  164. {
  165. return (isset($_POST[$var]))
  166. ? self::dispelMagicQuotes($_POST[$var])
  167. : $default;
  168. }
  169. /**
  170. * Creates a temporary filename for the lifetime of the script, and
  171. * (optionally) registers it to be deleted at request shutdown.
  172. *
  173. * @param string $prefix Prefix to make the temporary name more
  174. * recognizable.
  175. * @param boolean $delete Delete the file at the end of the request?
  176. * @param string $dir Directory to create the temporary file in.
  177. * @param boolean $secure If deleting the file, should we securely delete
  178. * the file by overwriting it with random data?
  179. *
  180. * @return string Returns the full path-name to the temporary file.
  181. * Returns false if a temp file could not be created.
  182. */
  183. static public function getTempFile($prefix = '', $delete = true, $dir = '',
  184. $secure = false)
  185. {
  186. $tempDir = (empty($dir) || !is_dir($dir))
  187. ? sys_get_temp_dir()
  188. : $dir;
  189. $tempFile = tempnam($tempDir, $prefix);
  190. // If the file was created, then register it for deletion and return.
  191. if (empty($tempFile)) {
  192. return false;
  193. }
  194. if ($delete) {
  195. self::deleteAtShutdown($tempFile, true, $secure);
  196. }
  197. return $tempFile;
  198. }
  199. /**
  200. * Creates a temporary filename with a specific extension for the lifetime
  201. * of the script, and (optionally) registers it to be deleted at request
  202. * shutdown.
  203. *
  204. * @param string $extension The file extension to use.
  205. * @param string $prefix Prefix to make the temporary name more
  206. * recognizable.
  207. * @param boolean $delete Delete the file at the end of the request?
  208. * @param string $dir Directory to create the temporary file in.
  209. * @param boolean $secure If deleting file, should we securely delete
  210. * the file by overwriting it with random data?
  211. *
  212. * @return string Returns the full path-name to the temporary file.
  213. * Returns false if a temporary file could not be created.
  214. */
  215. static public function getTempFileWithExtension($extension = '.tmp',
  216. $prefix = '',
  217. $delete = true, $dir = '',
  218. $secure = false)
  219. {
  220. $tempDir = (empty($dir) || !is_dir($dir))
  221. ? sys_get_temp_dir()
  222. : $dir;
  223. if (empty($tempDir)) {
  224. return false;
  225. }
  226. $windows = substr(PHP_OS, 0, 3) == 'WIN';
  227. $tries = 1;
  228. do {
  229. // Get a known, unique temporary file name.
  230. $sysFileName = tempnam($tempDir, $prefix);
  231. if ($sysFileName === false) {
  232. return false;
  233. }
  234. // tack on the extension
  235. $tmpFileName = $sysFileName . $extension;
  236. if ($sysFileName == $tmpFileName) {
  237. return $sysFileName;
  238. }
  239. // Move or point the created temporary file to the full filename
  240. // with extension. These calls fail if the new name already
  241. // exists.
  242. $fileCreated = ($windows ? @rename($sysFileName, $tmpFileName) : @link($sysFileName, $tmpFileName));
  243. if ($fileCreated) {
  244. if (!$windows) {
  245. unlink($sysFileName);
  246. }
  247. if ($delete) {
  248. self::deleteAtShutdown($tmpFileName, true, $secure);
  249. }
  250. return $tmpFileName;
  251. }
  252. unlink($sysFileName);
  253. } while (++$tries <= 5);
  254. return false;
  255. }
  256. /**
  257. * Creates a temporary directory in the system's temporary directory.
  258. *
  259. * @param boolean $delete Delete the temporary directory at the end of
  260. * the request?
  261. * @param string $temp_dir Use this temporary directory as the directory
  262. * where the temporary directory will be created.
  263. *
  264. * @return string The pathname to the new temporary directory.
  265. * Returns false if directory not created.
  266. */
  267. static public function createTempDir($delete = true, $temp_dir = null)
  268. {
  269. if (is_null($temp_dir)) {
  270. $temp_dir = sys_get_temp_dir();
  271. }
  272. if (empty($temp_dir)) {
  273. return false;
  274. }
  275. /* Get the first 8 characters of a random string to use as a temporary
  276. directory name. */
  277. do {
  278. $new_dir = $temp_dir . '/' . substr(base_convert(uniqid(mt_rand()), 10, 36), 0, 8);
  279. } while (file_exists($new_dir));
  280. $old_umask = umask(0000);
  281. if (!mkdir($new_dir, 0700)) {
  282. $new_dir = false;
  283. } elseif ($delete) {
  284. self::deleteAtShutdown($new_dir);
  285. }
  286. umask($old_umask);
  287. return $new_dir;
  288. }
  289. /**
  290. * Returns the canonical path of the string. Like PHP's built-in
  291. * realpath() except the directory need not exist on the local server.
  292. *
  293. * Algorithim loosely based on code from the Perl File::Spec::Unix module
  294. * (version 1.5).
  295. *
  296. * @param string $path A file path.
  297. *
  298. * @return string The canonicalized file path.
  299. */
  300. static public function realPath($path)
  301. {
  302. /* Standardize on UNIX directory separators. */
  303. if (!strncasecmp(PHP_OS, 'WIN', 3)) {
  304. $path = str_replace('\\', '/', $path);
  305. }
  306. /* xx////xx -> xx/xx
  307. * xx/././xx -> xx/xx */
  308. $path = preg_replace(array("|/+|", "@(/\.)+(/|\Z(?!\n))@"), array('/', '/'), $path);
  309. /* ./xx -> xx */
  310. if ($path != './') {
  311. $path = preg_replace("|^(\./)+|", '', $path);
  312. }
  313. /* /../../xx -> xx */
  314. $path = preg_replace("|^/(\.\./?)+|", '/', $path);
  315. /* xx/ -> xx */
  316. if ($path != '/') {
  317. $path = preg_replace("|/\Z(?!\n)|", '', $path);
  318. }
  319. /* /xx/.. -> / */
  320. while (strpos($path, '/..') !== false) {
  321. $path = preg_replace("|/[^/]+/\.\.|", '', $path);
  322. }
  323. return empty($path) ? '/' : $path;
  324. }
  325. /**
  326. * Removes given elements at request shutdown.
  327. *
  328. * If called with a filename will delete that file at request shutdown; if
  329. * called with a directory will remove that directory and all files in that
  330. * directory at request shutdown.
  331. *
  332. * If called with no arguments, return all elements to be deleted (this
  333. * should only be done by Horde_Util::_deleteAtShutdown()).
  334. *
  335. * The first time it is called, it initializes the array and registers
  336. * Horde_Util::_deleteAtShutdown() as a shutdown function - no need to do
  337. * so manually.
  338. *
  339. * The second parameter allows the unregistering of previously registered
  340. * elements.
  341. *
  342. * @param string $filename The filename to be deleted at the end of the
  343. * request.
  344. * @param boolean $register If true, then register the element for
  345. * deletion, otherwise, unregister it.
  346. * @param boolean $secure If deleting file, should we securely delete
  347. * the file?
  348. */
  349. static public function deleteAtShutdown($filename, $register = true,
  350. $secure = false)
  351. {
  352. /* Initialization of variables and shutdown functions. */
  353. if (!self::$_shutdownreg) {
  354. register_shutdown_function(array(__CLASS__, 'shutdown'));
  355. self::$_shutdownreg = true;
  356. }
  357. $ptr = &self::$_shutdowndata;
  358. if ($register) {
  359. if (@is_dir($filename)) {
  360. $ptr['dirs'][$filename] = true;
  361. } else {
  362. $ptr['files'][$filename] = true;
  363. }
  364. if ($secure) {
  365. $ptr['secure'][$filename] = true;
  366. }
  367. } else {
  368. unset($ptr['dirs'][$filename], $ptr['files'][$filename], $ptr['secure'][$filename]);
  369. }
  370. }
  371. /**
  372. * Deletes registered files at request shutdown.
  373. *
  374. * This function should never be called manually; it is registered as a
  375. * shutdown function by Horde_Util::deleteAtShutdown() and called
  376. * automatically at the end of the request.
  377. *
  378. * Contains code from gpg_functions.php.
  379. * Copyright 2002-2003 Braverock Ventures
  380. */
  381. static public function shutdown()
  382. {
  383. $ptr = &self::$_shutdowndata;
  384. foreach ($ptr['files'] as $file => $val) {
  385. /* Delete files */
  386. if ($val && file_exists($file)) {
  387. /* Should we securely delete the file by overwriting the data
  388. with a random string? */
  389. if (isset($ptr['secure'][$file])) {
  390. $filesize = filesize($file);
  391. $fp = fopen($file, 'r+');
  392. foreach (self::$patterns as $pattern) {
  393. $pattern = substr(str_repeat($pattern, floor($filesize / strlen($pattern)) + 1), 0, $filesize);
  394. fwrite($fp, $pattern);
  395. fseek($fp, 0);
  396. }
  397. fclose($fp);
  398. }
  399. @unlink($file);
  400. }
  401. }
  402. foreach ($ptr['dirs'] as $dir => $val) {
  403. /* Delete directories */
  404. if ($val && file_exists($dir)) {
  405. /* Make sure directory is empty. */
  406. $dir_class = dir($dir);
  407. while (false !== ($entry = $dir_class->read())) {
  408. if ($entry != '.' && $entry != '..') {
  409. @unlink($dir . '/' . $entry);
  410. }
  411. }
  412. $dir_class->close();
  413. @rmdir($dir);
  414. }
  415. }
  416. }
  417. /**
  418. * Caches the result of extension_loaded() calls.
  419. *
  420. * @param string $ext The extension name.
  421. *
  422. * @return boolean Is the extension loaded?
  423. */
  424. static public function extensionExists($ext)
  425. {
  426. if (!isset(self::$_cache[$ext])) {
  427. self::$_cache[$ext] = extension_loaded($ext);
  428. }
  429. return self::$_cache[$ext];
  430. }
  431. /**
  432. * Tries to load a PHP extension, behaving correctly for all operating
  433. * systems.
  434. *
  435. * @param string $ext The extension to load.
  436. *
  437. * @return boolean True if the extension is now loaded, false if not.
  438. * True can mean that the extension was already loaded,
  439. * OR was loaded dynamically.
  440. */
  441. static public function loadExtension($ext)
  442. {
  443. /* If $ext is already loaded, our work is done. */
  444. if (self::extensionExists($ext)) {
  445. return true;
  446. }
  447. /* See if we can call dl() at all, by the current ini settings.
  448. * dl() has been removed in some PHP 5.3 SAPIs. */
  449. if ((ini_get('enable_dl') != 1) ||
  450. (ini_get('safe_mode') == 1) ||
  451. !function_exists('dl')) {
  452. return false;
  453. }
  454. if (!strncasecmp(PHP_OS, 'WIN', 3)) {
  455. $suffix = 'dll';
  456. } else {
  457. switch (PHP_OS) {
  458. case 'HP-UX':
  459. $suffix = 'sl';
  460. break;
  461. case 'AIX':
  462. $suffix = 'a';
  463. break;
  464. case 'OSX':
  465. $suffix = 'bundle';
  466. break;
  467. default:
  468. $suffix = 'so';
  469. }
  470. }
  471. return dl($ext . '.' . $suffix) || dl('php_' . $ext . '.' . $suffix);
  472. }
  473. /**
  474. * Utility function to obtain PATH_INFO information.
  475. *
  476. * @return string The PATH_INFO string.
  477. */
  478. static public function getPathInfo()
  479. {
  480. if (isset($_SERVER['PATH_INFO']) &&
  481. (strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') === false)) {
  482. return $_SERVER['PATH_INFO'];
  483. } elseif (isset($_SERVER['REQUEST_URI']) &&
  484. isset($_SERVER['SCRIPT_NAME'])) {
  485. $search = Horde_String::common($_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI']);
  486. if (substr($search, -1) == '/') {
  487. $search = substr($search, 0, -1);
  488. }
  489. $search = array($search);
  490. if (!empty($_SERVER['QUERY_STRING'])) {
  491. // We can't use QUERY_STRING directly because URL rewriting
  492. // might add more parameters to the query string than those
  493. // from the request URI.
  494. $url = parse_url($_SERVER['REQUEST_URI']);
  495. if (!empty($url['query'])) {
  496. $search[] = '?' . $url['query'];
  497. }
  498. }
  499. $path = str_replace($search, '', $_SERVER['REQUEST_URI']);
  500. if ($path == '/') {
  501. $path = '';
  502. }
  503. return $path;
  504. }
  505. return '';
  506. }
  507. }