PageRenderTime 45ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/core/functions.inc.php

https://github.com/ifthisandthat/devbox
PHP | 1604 lines | 1190 code | 128 blank | 286 comment | 159 complexity | a8054b376d05004890134fd24f2084ac MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. // Class Autloader
  3. spl_autoload_register('framework_autoload');
  4. function framework_autoload($class_name)
  5. {
  6. $filename = LIBRARY . '/class.' . strtolower($class_name) . '.php';
  7. if(file_exists($filename))
  8. require $filename;
  9. }
  10. // SET DEFAULT FOR A GIVEN VARIABLE
  11. function set_default(&$var, $default="")
  12. {
  13. if ($var === TRUE) return TRUE;
  14. $var = (!isset($var) || (($var == "" || $var == "0000-00-00 00:00:00" || $var == "0000-00-00"))) ? $default : $var;
  15. return $var;
  16. }
  17. function shuffle_assoc($array)
  18. {
  19. $keys = array_keys($array);
  20. shuffle($keys);
  21. return array_merge(array_flip($keys) , $array);
  22. }
  23. /**
  24. * Convert UTF-8 characters to their equivalent ISO map
  25. *
  26. * @param string $str
  27. * @return string
  28. * @author j4kp07
  29. */
  30. function utf82iso($str)
  31. {
  32. $output = str_replace('™', ' (TM)', $str);
  33. $output = str_replace('None', '', $output);
  34. return iconv("UTF-8", "ISO-8859-1//TRANSLIT", $output);
  35. }
  36. /**
  37. * Searches the array (recursively) for a given value and returns the corresponding key if successful
  38. *
  39. * @param string $needle
  40. * @param array $haystack
  41. * @param boolean $partial_matches
  42. * @param boolean $search_keys
  43. * @author j4kp07
  44. */
  45. function array_find_r($needle, $haystack, $partial_matches = false, $search_keys = false)
  46. {
  47. if(!is_array($haystack)) return false;
  48. foreach($haystack as $key=>$value)
  49. {
  50. $what = ($search_keys) ? $key : $value;
  51. if($needle===$what) return $key;
  52. else if($partial_matches && @strpos($what, $needle)!==false) return $key;
  53. else if(is_array($value) && self::array_find_r($needle, $value, $partial_matches, $search_keys)!==false) return $key;
  54. }
  55. return false;
  56. }
  57. /**
  58. * Convert an object to an array
  59. *
  60. * @param string $object
  61. * @return array
  62. * @author j4kp07
  63. */
  64. function object2array($object)
  65. {
  66. $array = array();
  67. if (is_object($object))
  68. {
  69. $array = $object->result_array();
  70. }
  71. return $array;
  72. }
  73. /**
  74. * Convert an array to a comma delimited list
  75. *
  76. * @param string $array
  77. * @return string
  78. * @author j4kp07
  79. */
  80. function array2list($array = '')
  81. {
  82. $output = '';
  83. if(is_array($array))
  84. {
  85. foreach ($array as $key => $value)
  86. {
  87. $output .= "{$value},";
  88. }
  89. $output = trim($output, ',');
  90. }
  91. return $output;
  92. }
  93. /**
  94. * Convert an array to an object
  95. *
  96. * @param string $array
  97. * @return object
  98. * @author j4kp07
  99. */
  100. function array2object($array)
  101. {
  102. $object = new stdClass();
  103. if (is_array($array) && count($array) > 0)
  104. {
  105. foreach ($array as $name=>$value)
  106. {
  107. $name = strtolower(trim($name));
  108. if (!empty($name))
  109. {
  110. $object->$name = $value;
  111. }
  112. }
  113. }
  114. return $object;
  115. }
  116. /**
  117. * Sort and array of objects by its properties
  118. *
  119. * @param object $object
  120. * @param object $property
  121. * @return object
  122. * @author j4kp07
  123. */
  124. function osort(&$object, $property)
  125. {
  126. usort($object, create_function('$a,$b', 'if ($a->' . $property . '== $b->' . $property .') return 0; return ($a->' . $property . '< $b->' . $property .') ? -1 : 1;'));
  127. }
  128. /**
  129. * Applies the callback to the elements of the given arrays (recursively)
  130. *
  131. * @param callback $func
  132. * @param array $arr
  133. * @return void
  134. * @author j4kp07
  135. */
  136. function array_map_recursive($func, $arr)
  137. {
  138. $result = array();
  139. do
  140. {
  141. $key = key($arr);
  142. if (is_array(current($arr)))
  143. {
  144. $result[$key] = self::array_map_recursive($func, $arr[$key]);
  145. }
  146. else
  147. {
  148. $result[$key] = self::$func(current($arr));
  149. }
  150. }
  151. while (next($arr) !== false);
  152. return $result;
  153. }
  154. /**
  155. * Quote string with slashes (recursively)
  156. *
  157. * @param array $arr
  158. * @return array
  159. * @author j4kp07
  160. */
  161. function addslashes_array($arr)
  162. {
  163. if(is_array($arr))
  164. {
  165. $tmp = array();
  166. foreach ($arr as $key1 => $val)
  167. {
  168. $tmp[$key1] = self::addslashes_array($val);
  169. }
  170. return $tmp;
  171. }
  172. else
  173. {
  174. return addslashes(stripslashes($arr));
  175. }
  176. }
  177. /**
  178. * Un-quotes a quoted string (recursively)
  179. *
  180. * @param array $arr
  181. * @return array
  182. * @author j4kp07
  183. */
  184. function stripslashes_array($arr)
  185. {
  186. if(is_array($arr))
  187. {
  188. $tmp = array();
  189. foreach ($arr as $key1 => $val)
  190. {
  191. $tmp[$key1] = self::stripslashes_array($val);
  192. }
  193. return $tmp;
  194. }
  195. else
  196. {
  197. return stripslashes($arr);
  198. }
  199. }
  200. /**
  201. * Tests if the current request is an AJAX request by checking the X-Requested-With HTTP
  202. * request header that most popular JS frameworks now set for AJAX calls.
  203. *
  204. * @return boolean
  205. */
  206. function is_ajax()
  207. {
  208. return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) AND strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
  209. }
  210. // Returns the first $num characters of $strText with a trailing $strTrail
  211. function max_chars ($str, $num, $trail)
  212. {
  213. $wsCount = 0;
  214. $intTempSize = 0;
  215. $intTotalLen = 0;
  216. $intLength = $num - strlen($trail);
  217. $strTemp = NULL;
  218. if (strlen($str) > $num) :
  219. $arrTemp = explode(" ", $str);
  220. foreach ($arrTemp as $x) :
  221. $strTemp .= (strlen($strTemp) <= $num) ? ' ' . $x : $strTemp;
  222. endforeach;
  223. $output = $strTemp . $trail;
  224. else :
  225. $output = $strText;
  226. endif;
  227. return $output;
  228. }
  229. // Returns the first $num words of $str
  230. function max_words($str, $num, $suffix = '')
  231. {
  232. $words = explode(' ', $str);
  233. if(count($words) < $num) :
  234. return $str;
  235. else :
  236. return implode(' ', array_slice($words, 0, $num)) . $suffix;
  237. endif;
  238. }
  239. // GENERATE RANDOM HASH
  240. function get_hash($num)
  241. {
  242. $chars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  243. $max = strlen($chars)-1;
  244. for($i=0; $i<$num; $i++) :
  245. $output .= $chars[rand(0,$max)];
  246. endfor;
  247. return $output;
  248. }
  249. // GET SUFFIX FOR AN INTEGER
  250. function get_suffix($x)
  251. {
  252. $x = substr($x,-1);
  253. if($x == 1) :
  254. $output = "st";
  255. elseif ($x == 2) :
  256. $output = "nd";
  257. elseif($x == 3) :
  258. $output = "rd";
  259. else :
  260. $output = "th";
  261. endif;
  262. return $output;
  263. }
  264. function get_time($name, $id, $class, $thistime)
  265. {
  266. echo "<select name=\"{$name}\" id=\"{$id}\" class=\"{$class}\">";
  267. $i = 0;
  268. $time = date('H:i',mktime(0, 0, 0));
  269. while($i < 1440) :
  270. $v = date('H:i:s',mktime(0, 0 + $i, 0));
  271. $k = date('g:i a',mktime(0, 0 + $i, 0));
  272. $selected = ($v == $thistime) ? 'selected' : NULL;
  273. echo "<option value=\"{$v}\" {$selected}>{$k}</option>\r\n";
  274. $i = $i + 15;
  275. endwhile;
  276. echo "</select>";
  277. }
  278. // Grabs the contents of a remote URL. Can perform basic authentication if un/pw are provided.
  279. function get_url($url, $username = null, $password = null)
  280. {
  281. if(function_exists('curl_init'))
  282. {
  283. $ch = curl_init();
  284. if(!is_null($username) && !is_null($password))
  285. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Authorization: Basic ' . base64_encode("$username:$password")));
  286. curl_setopt($ch, CURLOPT_URL, $url);
  287. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  288. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
  289. $html = curl_exec($ch);
  290. curl_close($ch);
  291. return $html;
  292. }
  293. elseif(ini_get('allow_url_fopen') == true)
  294. {
  295. if(!is_null($username) && !is_null($password))
  296. $url = str_replace("://", "://$username:$password@", $url);
  297. $html = file_get_contents($url);
  298. return $html;
  299. }
  300. else
  301. {
  302. // Cannot open url. Either install curl-php or set allow_url_fopen = true in php.ini
  303. return false;
  304. }
  305. }
  306. // SEND EMAIL WITH/WITHOUT ATTACHMENTS
  307. function sendmail($to, $from, $subject, $msg, $file=NULL)
  308. {
  309. $uid = md5(uniqid(time()));
  310. $headers = "MIME-Version: 1.0\r\n";
  311. $headers .= "From: {$from}\r\n";
  312. $headers .= "Reply-To: {$from}\r\n";
  313. $headers .= "X-Mailer: PHP/".phpversion()."\r\n";
  314. if($file == NULL) :
  315. $headers .= 'Content-type: text/html; charset=iso-8859-1'."\r\n";
  316. else :
  317. $file_size = filesize($file);
  318. $handle = fopen($file, "r");
  319. $content = fread($handle, $file_size);
  320. fclose($handle);
  321. $content = chunk_split(base64_encode($content));
  322. $headers .= "Content-Type: multipart/mixed; boundary=\"".$uid."\"\r\n\r\n";
  323. $headers .= "This is a multi-part message in MIME format.\r\n";
  324. $headers .= "--{$uid}\r\n";
  325. $headers .= "Content-type:text/html; charset=iso-8859-1\r\n";
  326. $headers .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
  327. $headers .= "{$msg}\r\n\r\n";
  328. $headers .= "--{$uid}\r\n";
  329. $headers .= "Content-Type: application/pdf; name=\"{$attachment}\"\r\n";
  330. // $headers .= "Content-Type: application/octet-stream; name=\"{$attachment}\"\r\n";
  331. $headers .= "Content-Transfer-Encoding: base64\r\n";
  332. $headers .= "Content-Disposition: attachment; filename=\"{$attachment}\"\r\n\r\n";
  333. $headers .= "{$content}\r\n\r\n";
  334. $headers .= "--{$uid}--";
  335. endif;
  336. $pattern = '/^([a-z0-9])(([-a-z0-9._\+])*([a-z0-9]))*\@([a-z0-9])' . '(([a-z0-9-])*([a-z0-9]))+' . '(\.([a-z0-9])([-a-z0-9_-])?([a-z0-9])+)+$/i';
  337. if(preg_match($pattern, $to)) :
  338. mail($to, $subject, $msg, $headers);
  339. endif;
  340. }
  341. function datediff($month,$day,$year)
  342. {
  343. $today = time();
  344. $thisdate = mktime(0,0,0,$month,$day,$year);
  345. $dateDiff = $today - $thisdate;
  346. $fullyears = floor($dateDiff/(60*60*24*365));
  347. return $fullyears;
  348. }
  349. function printr($var)
  350. {
  351. $output = print_r($var, true);
  352. $output = str_replace("\n", "<br>", $output);
  353. $output = str_replace(' ', '&nbsp;', $output);
  354. echo "<div style='font-family:courier;'>$output</div>";
  355. }
  356. // Given a string such as "comment_123" or "id_57", it returns the final, numeric id.
  357. function split_id($str)
  358. {
  359. return match('/[_-]([0-9]+)$/', $str, 1);
  360. }
  361. // Creates a friendly URL slug from a string
  362. function slugify($str)
  363. {
  364. $str = preg_replace('/[^a-zA-Z0-9 -]/', '', $str);
  365. $str = strtolower(str_replace(' ', '-', trim($str)));
  366. $str = preg_replace('/-+/', '-', $str);
  367. return $str;
  368. }
  369. // Computes the *full* URL of the current page (protocol, server, path, query parameters, etc)
  370. function full_url()
  371. {
  372. $s = empty($_SERVER['HTTPS']) ? '' : ($_SERVER['HTTPS'] == 'on') ? 's' : '';
  373. $protocol = substr(strtolower($_SERVER['SERVER_PROTOCOL']), 0, strpos(strtolower($_SERVER['SERVER_PROTOCOL']), '/')) . $s;
  374. $port = ($_SERVER['SERVER_PORT'] == '80') ? '' : (":".$_SERVER['SERVER_PORT']);
  375. return $protocol . "://" . $_SERVER['HTTP_HOST'] . $port . $_SERVER['REQUEST_URI'];
  376. }
  377. // Returns an English representation of a date
  378. // Graciously stolen from http://ejohn.org/files/pretty.js
  379. function time2str($ts)
  380. {
  381. if(!ctype_digit($ts))
  382. $ts = strtotime($ts);
  383. $diff = time() - $ts;
  384. if($diff == 0)
  385. return 'now';
  386. elseif($diff > 0)
  387. {
  388. $day_diff = floor($diff / 86400);
  389. if($day_diff == 0)
  390. {
  391. if($diff < 60) return 'just now';
  392. if($diff < 120) return '1 minute ago';
  393. if($diff < 3600) return floor($diff / 60) . ' minutes ago';
  394. if($diff < 7200) return '1 hour ago';
  395. if($diff < 86400) return floor($diff / 3600) . ' hours ago';
  396. }
  397. if($day_diff == 1) return 'Yesterday';
  398. if($day_diff < 7) return $day_diff . ' days ago';
  399. if($day_diff < 31) return ceil($day_diff / 7) . ' weeks ago';
  400. if($day_diff < 60) return 'last month';
  401. $ret = date('F Y', $ts);
  402. return ($ret == 'December 1969') ? '' : $ret;
  403. }
  404. else
  405. {
  406. $diff = abs($diff);
  407. $day_diff = floor($diff / 86400);
  408. if($day_diff == 0)
  409. {
  410. if($diff < 120) return 'in a minute';
  411. if($diff < 3600) return 'in ' . floor($diff / 60) . ' minutes';
  412. if($diff < 7200) return 'in an hour';
  413. if($diff < 86400) return 'in ' . floor($diff / 3600) . ' hours';
  414. }
  415. if($day_diff == 1) return 'Tomorrow';
  416. if($day_diff < 4) return date('l', $ts);
  417. if($day_diff < 7 + (7 - date('w'))) return 'next week';
  418. if(ceil($day_diff / 7) < 4) return 'in ' . ceil($day_diff / 7) . ' weeks';
  419. if(date('n', $ts) == date('n') + 1) return 'next month';
  420. $ret = date('F Y', $ts);
  421. return ($ret == 'December 1969') ? '' : $ret;
  422. }
  423. }
  424. // Returns an array representation of the given calendar month.
  425. // The array values are timestamps which allow you to easily format
  426. // and manipulate the dates as needed.
  427. function calendar($month = null, $year = null)
  428. {
  429. if(is_null($month)) $month = date('n');
  430. if(is_null($year)) $year = date('Y');
  431. $first = mktime(0, 0, 0, $month, 1, $year);
  432. $last = mktime(23, 59, 59, $month, date('t', $first), $year);
  433. $start = $first - (86400 * date('w', $first));
  434. $stop = $last + (86400 * (7 - date('w', $first)));
  435. $out = array();
  436. while($start < $stop)
  437. {
  438. $week = array();
  439. if($start > $last) break;
  440. for($i = 0; $i < 7; $i++)
  441. {
  442. $week[$i] = $start;
  443. $start += 86400;
  444. }
  445. $out[] = $week;
  446. }
  447. return $out;
  448. }
  449. // Processes mod_rewrite URLs into key => value pairs
  450. // See .htacess for more info.
  451. function pick_off($grab_first = false, $sep = '/')
  452. {
  453. $ret = array();
  454. $arr = explode($sep, trim($_SERVER['REQUEST_URI'], $sep));
  455. if($grab_first) $ret[0] = array_shift($arr);
  456. while(count($arr) > 0)
  457. $ret[array_shift($arr)] = array_shift($arr);
  458. return (count($ret) > 0) ? $ret : false;
  459. }
  460. // More robust strict date checking for string representations
  461. function chkdate($str)
  462. {
  463. // Requires PHP 5.2
  464. if(function_exists('date_parse'))
  465. {
  466. $info = date_parse($str);
  467. if($info !== false && $info['error_count'] == 0)
  468. {
  469. if(checkdate($info['month'], $info['day'], $info['year']))
  470. return true;
  471. }
  472. return false;
  473. }
  474. // Else, for PHP < 5.2
  475. return strtotime($str);
  476. }
  477. // Converts a date/timestamp into the specified format
  478. function dater($date = null, $format = null)
  479. {
  480. if(is_null($format))
  481. $format = 'Y-m-d H:i:s';
  482. if(is_null($date))
  483. $date = time();
  484. if(is_int($date))
  485. return date($format, $date);
  486. if(is_float($date))
  487. return date($format, $date);
  488. if(is_string($date)) {
  489. if(ctype_digit($date) === true)
  490. return date($format, $date);
  491. if((preg_match('/[^0-9.]/', $date) == 0) && (substr_count($date, '.') <= 1))
  492. return date($format, floatval($date));
  493. return date($format, strtotime($date));
  494. }
  495. // If $date is anything else, you're doing something wrong,
  496. // so just let PHP error out however it wants.
  497. return date($format, $date);
  498. }
  499. function format_date($d,$format)
  500. {
  501. $y = substr($d,0,4);
  502. $m = str_pad(substr($d,4,2),'/',STR_PAD_BOTH);
  503. $d = substr($d,6,2);
  504. $output = (strlen($d) > 0) ? date($format,strtotime($y.$m.$d)):NULL;
  505. return $output;
  506. }
  507. function format_phone($n)
  508. {
  509. $n = preg_replace("/[^0-9]/", "", $n);
  510. if(strlen($n) == 7) :
  511. return preg_replace("/([0-9]{3})([0-9]{4})/", "$1-$2", $n);
  512. elseif (strlen($n) == 10) :
  513. return preg_replace("/([0-9]{3})([0-9]{3})([0-9]{4})/", "$1-$2-$3", $n);
  514. else :
  515. return $n;
  516. endif;
  517. }
  518. // Formats a given number of seconds into proper mm:ss format
  519. function format_time($seconds)
  520. {
  521. return floor($seconds / 60) . ':' . str_pad($seconds % 60, 2, '0');
  522. }
  523. function format_url($url)
  524. {
  525. if (!empty($url)) :
  526. $output = (preg_match("/http(s?):\/\//", $url)) ? $url : "http://{$url}";
  527. else :
  528. $output = FALSE;
  529. endif;
  530. return $output;
  531. }
  532. // Outputs hour, minute, am/pm dropdown boxes
  533. function hourmin($hid = 'hour', $mid = 'minute', $pid = 'ampm', $hval = null, $mval = null, $pval = null)
  534. {
  535. // Dumb hack to let you just pass in a timestamp instead
  536. if(func_num_args() == 1)
  537. {
  538. list($hval, $mval, $pval) = explode(' ', date('g i a', strtotime($hid)));
  539. $hid = 'hour';
  540. $mid = 'minute';
  541. $aid = 'ampm';
  542. }
  543. else
  544. {
  545. if(is_null($hval)) $hval = date('h');
  546. if(is_null($mval)) $mval = date('i');
  547. if(is_null($pval)) $pval = date('a');
  548. }
  549. $hours = array(12, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11);
  550. $out = "<select name='$hid' id='$hid'>";
  551. foreach($hours as $hour)
  552. if(intval($hval) == intval($hour)) $out .= "<option value='$hour' selected>$hour</option>";
  553. else $out .= "<option value='$hour'>$hour</option>";
  554. $out .= "</select>";
  555. $minutes = array('00', 15, 30, 45);
  556. $out .= "<select name='$mid' id='$mid'>";
  557. foreach($minutes as $minute)
  558. if(intval($mval) == intval($minute)) $out .= "<option value='$minute' selected>$minute</option>";
  559. else $out .= "<option value='$minute'>$minute</option>";
  560. $out .= "</select>";
  561. $out .= "<select name='$pid' id='$pid'>";
  562. $out .= "<option value='am'>am</option>";
  563. if($pval == 'pm') $out .= "<option value='pm' selected>pm</option>";
  564. else $out .= "<option value='pm'>pm</option>";
  565. $out .= "</select>";
  566. return $out;
  567. }
  568. // Returns the HTML for a month, day, and year dropdown boxes.
  569. // You can set the default date by passing in a timestamp OR a parseable date string.
  570. // $prefix_ will be appened to the name/id's of each dropdown, allowing for multiple calls in the same form.
  571. // $output_format lets you specify which dropdowns appear and in what order.
  572. function mdy($date = null, $prefix = null, $output_format = 'm d y')
  573. {
  574. if(is_null($date)) $date = time();
  575. if(!ctype_digit($date)) $date = strtotime($date);
  576. if(!is_null($prefix)) $prefix .= '_';
  577. list($yval, $mval, $dval) = explode(' ', date('Y n j', $date));
  578. $month_dd = "<select name='{$prefix}month' id='{$prefix}month'>";
  579. for($i = 1; $i <= 12; $i++)
  580. {
  581. $selected = ($mval == $i) ? ' selected="selected"' : '';
  582. $month_dd .= "<option value='$i'$selected>" . date('F', mktime(0, 0, 0, $i, 1, 2000)) . "</option>";
  583. }
  584. $month_dd .= "</select>";
  585. $day_dd = "<select name='{$prefix}day' id='{$prefix}day'>";
  586. for($i = 1; $i <= 31; $i++)
  587. {
  588. $selected = ($dval == $i) ? ' selected="selected"' : '';
  589. $day_dd .= "<option value='$i'$selected>$i</option>";
  590. }
  591. $day_dd .= "</select>";
  592. $year_dd = "<select name='{$prefix}year' id='{$prefix}year'>";
  593. for($i = date('Y'); $i < date('Y') + 10; $i++)
  594. {
  595. $selected = ($yval == $i) ? ' selected="selected"' : '';
  596. $year_dd .= "<option value='$i'$selected>$i</option>";
  597. }
  598. $year_dd .= "</select>";
  599. $trans = array('m' => $month_dd, 'd' => $day_dd, 'y' => $year_dd);
  600. return strtr($output_format, $trans);
  601. }
  602. // Redirects user to $url
  603. function redirect($url = null)
  604. {
  605. if(is_null($url)) $url = $_SERVER['PHP_SELF'];
  606. header("Location: $url");
  607. exit();
  608. }
  609. // Ensures $str ends with a single /
  610. function slash($str)
  611. {
  612. return rtrim($str, '/') . '/';
  613. }
  614. // Ensures $str DOES NOT end with a /
  615. function unslash($str)
  616. {
  617. return rtrim($str, '/');
  618. }
  619. // Returns an array of the values of the specified column from a multi-dimensional array
  620. function gimme($arr, $key = null)
  621. {
  622. if(is_null($key))
  623. $key = current(array_keys($arr));
  624. $out = array();
  625. foreach($arr as $a)
  626. $out[] = $a[$key];
  627. return $out;
  628. }
  629. // Fixes MAGIC_QUOTES
  630. function fix_slashes($arr = '')
  631. {
  632. if(is_null($arr) || $arr == '') return null;
  633. if(!get_magic_quotes_gpc()) return $arr;
  634. return is_array($arr) ? array_map('fix_slashes', $arr) : stripslashes($arr);
  635. }
  636. // Serves an external document for download as an HTTP attachment.
  637. function download_document($filename, $mimetype = 'application/octet-stream')
  638. {
  639. if(!file_exists($filename) || !is_readable($filename)) return false;
  640. $base = basename($filename);
  641. header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
  642. header("Content-Disposition: attachment; filename=$base");
  643. header("Content-Length: " . filesize($filename));
  644. header("Content-Type: $mimetype");
  645. readfile($filename);
  646. exit();
  647. }
  648. // Retrieves the filesize of a remote file.
  649. function remote_filesize($url, $user = null, $pw = null)
  650. {
  651. $ch = curl_init($url);
  652. curl_setopt($ch, CURLOPT_HEADER, 1);
  653. curl_setopt($ch, CURLOPT_NOBODY, 1);
  654. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  655. if(!is_null($user) && !is_null($pw))
  656. {
  657. $headers = array('Authorization: Basic ' . base64_encode("$user:$pw"));
  658. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  659. }
  660. $head = curl_exec($ch);
  661. curl_close($ch);
  662. preg_match('/Content-Length:\s([0-9].+?)\s/', $head, $matches);
  663. return isset($matches[1]) ? $matches[1] : false;
  664. }
  665. // Inserts a string within another string at a specified location
  666. function str_insert($needle, $haystack, $location)
  667. {
  668. $front = substr($haystack, 0, $location);
  669. $back = substr($haystack, $location);
  670. return $front . $needle . $back;
  671. }
  672. // Outputs a filesize in human readable format.
  673. function bytes2str($val, $round = 0)
  674. {
  675. $unit = array('','K','M','G','T','P','E','Z','Y');
  676. while($val >= 1000)
  677. {
  678. $val /= 1024;
  679. array_shift($unit);
  680. }
  681. return round($val, $round) . array_shift($unit) . 'B';
  682. }
  683. // Returns the user's browser info.
  684. // browscap.ini must be available for this to work.
  685. // See the PHP manual for more details.
  686. function browser_info()
  687. {
  688. $info = get_browser(null, true);
  689. $browser = $info['browser'] . ' ' . $info['version'];
  690. $os = $info['platform'];
  691. $ip = $_SERVER['REMOTE_ADDR'];
  692. return array('ip' => $ip, 'browser' => $browser, 'os' => $os);
  693. }
  694. // Quick wrapper for preg_match
  695. function match($regex, $str, $i = 0)
  696. {
  697. if(preg_match($regex, $str, $match) == 1)
  698. return $match[$i];
  699. else
  700. return false;
  701. }
  702. // Returns the lat, long of an address via Yahoo!'s geocoding service.
  703. // You'll need an App ID, which is available from here:
  704. // http://developer.yahoo.com/maps/rest/V1/geocode.html
  705. // Note: needs to be updated to use PlaceFinder instead.
  706. function geocode($location, $appid)
  707. {
  708. $location = urlencode($location);
  709. $appid = urlencode($appid);
  710. $data = file_get_contents("http://local.yahooapis.com/MapsService/V1/geocode?output=php&appid=$appid&location=$location");
  711. $data = unserialize($data);
  712. if($data === false) return false;
  713. $data = $data['ResultSet']['Result'];
  714. return array('lat' => $data['Latitude'], 'lng' => $data['Longitude']);
  715. }
  716. // A stub for Yahoo!'s reverse geocoding service
  717. // http://developer.yahoo.com/geo/placefinder/
  718. function reverse_geocode($lat, $lng)
  719. {
  720. }
  721. // Quick and dirty wrapper for curl scraping.
  722. function curl($url, $referer = null, $post = null)
  723. {
  724. static $tmpfile;
  725. if(!isset($tmpfile) || ($tmpfile == '')) $tmpfile = tempnam('/tmp', 'FOO');
  726. $ch = curl_init($url);
  727. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  728. curl_setopt($ch, CURLOPT_COOKIEFILE, $tmpfile);
  729. curl_setopt($ch, CURLOPT_COOKIEJAR, $tmpfile);
  730. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  731. curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1) Gecko/20061024 BonEcho/2.0");
  732. // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  733. // curl_setopt($ch, CURLOPT_VERBOSE, 1);
  734. if($referer) curl_setopt($ch, CURLOPT_REFERER, $referer);
  735. if(!is_null($post))
  736. {
  737. curl_setopt($ch, CURLOPT_POST, true);
  738. curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
  739. }
  740. $html = curl_exec($ch);
  741. // $last_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
  742. return $html;
  743. }
  744. // Accepts any number of arguments and returns the first non-empty one
  745. function pick()
  746. {
  747. foreach(func_get_args() as $arg)
  748. if(!empty($arg))
  749. return $arg;
  750. return '';
  751. }
  752. // Secure a PHP script using basic HTTP authentication
  753. function http_auth($un, $pw, $realm = "Secured Area")
  754. {
  755. if(!(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) && $_SERVER['PHP_AUTH_USER'] == $un && $_SERVER['PHP_AUTH_PW'] == $pw))
  756. {
  757. header('WWW-Authenticate: Basic realm="' . $realm . '"');
  758. header('Status: 401 Unauthorized');
  759. exit();
  760. }
  761. }
  762. // Returns a file's mimetype based on its extension
  763. function mime_type($filename, $default = 'application/octet-stream')
  764. {
  765. $mime_types = array(
  766. '323' => 'text/h323',
  767. 'acx' => 'application/internet-property-stream',
  768. 'ai' => 'application/postscript',
  769. 'aif' => 'audio/x-aiff',
  770. 'aifc' => 'audio/x-aiff',
  771. 'aiff' => 'audio/x-aiff',
  772. 'asf' => 'video/x-ms-asf',
  773. 'asr' => 'video/x-ms-asf',
  774. 'asx' => 'video/x-ms-asf',
  775. 'au' => 'audio/basic',
  776. 'avi' => 'video/x-msvideo',
  777. 'axs' => 'application/olescript',
  778. 'bas' => 'text/plain',
  779. 'bcpio' => 'application/x-bcpio',
  780. 'bin' => 'application/octet-stream',
  781. 'bmp' => 'image/bmp',
  782. 'c' => 'text/plain',
  783. 'cat' => 'application/vnd.ms-pkiseccat',
  784. 'cdf' => 'application/x-cdf',
  785. 'cer' => 'application/x-x509-ca-cert',
  786. 'class' => 'application/octet-stream',
  787. 'clp' => 'application/x-msclip',
  788. 'cmx' => 'image/x-cmx',
  789. 'cod' => 'image/cis-cod',
  790. 'cpio' => 'application/x-cpio',
  791. 'crd' => 'application/x-mscardfile',
  792. 'crl' => 'application/pkix-crl',
  793. 'crt' => 'application/x-x509-ca-cert',
  794. 'csh' => 'application/x-csh',
  795. 'css' => 'text/css',
  796. 'dcr' => 'application/x-director',
  797. 'der' => 'application/x-x509-ca-cert',
  798. 'dir' => 'application/x-director',
  799. 'dll' => 'application/x-msdownload',
  800. 'dms' => 'application/octet-stream',
  801. 'doc' => 'application/msword',
  802. 'dot' => 'application/msword',
  803. 'dvi' => 'application/x-dvi',
  804. 'dxr' => 'application/x-director',
  805. 'eps' => 'application/postscript',
  806. 'etx' => 'text/x-setext',
  807. 'evy' => 'application/envoy',
  808. 'exe' => 'application/octet-stream',
  809. 'fif' => 'application/fractals',
  810. 'flac' => 'audio/flac',
  811. 'flr' => 'x-world/x-vrml',
  812. 'gif' => 'image/gif',
  813. 'gtar' => 'application/x-gtar',
  814. 'gz' => 'application/x-gzip',
  815. 'h' => 'text/plain',
  816. 'hdf' => 'application/x-hdf',
  817. 'hlp' => 'application/winhlp',
  818. 'hqx' => 'application/mac-binhex40',
  819. 'hta' => 'application/hta',
  820. 'htc' => 'text/x-component',
  821. 'htm' => 'text/html',
  822. 'html' => 'text/html',
  823. 'htt' => 'text/webviewhtml',
  824. 'ico' => 'image/x-icon',
  825. 'ief' => 'image/ief',
  826. 'iii' => 'application/x-iphone',
  827. 'ins' => 'application/x-internet-signup',
  828. 'isp' => 'application/x-internet-signup',
  829. 'jfif' => 'image/pipeg',
  830. 'jpe' => 'image/jpeg',
  831. 'jpeg' => 'image/jpeg',
  832. 'jpg' => 'image/jpeg',
  833. 'js' => 'application/x-javascript',
  834. 'latex' => 'application/x-latex',
  835. 'lha' => 'application/octet-stream',
  836. 'lsf' => 'video/x-la-asf',
  837. 'lsx' => 'video/x-la-asf',
  838. 'lzh' => 'application/octet-stream',
  839. 'm13' => 'application/x-msmediaview',
  840. 'm14' => 'application/x-msmediaview',
  841. 'm3u' => 'audio/x-mpegurl',
  842. 'man' => 'application/x-troff-man',
  843. 'mdb' => 'application/x-msaccess',
  844. 'me' => 'application/x-troff-me',
  845. 'mht' => 'message/rfc822',
  846. 'mhtml' => 'message/rfc822',
  847. 'mid' => 'audio/mid',
  848. 'mny' => 'application/x-msmoney',
  849. 'mov' => 'video/quicktime',
  850. 'movie' => 'video/x-sgi-movie',
  851. 'mp2' => 'video/mpeg',
  852. 'mp3' => 'audio/mpeg',
  853. 'mpa' => 'video/mpeg',
  854. 'mpe' => 'video/mpeg',
  855. 'mpeg' => 'video/mpeg',
  856. 'mpg' => 'video/mpeg',
  857. 'mpp' => 'application/vnd.ms-project',
  858. 'mpv2' => 'video/mpeg',
  859. 'ms' => 'application/x-troff-ms',
  860. 'mvb' => 'application/x-msmediaview',
  861. 'nws' => 'message/rfc822',
  862. 'oda' => 'application/oda',
  863. 'oga' => 'audio/ogg',
  864. 'ogg' => 'audio/ogg',
  865. 'ogv' => 'video/ogg',
  866. 'ogx' => 'application/ogg',
  867. 'p10' => 'application/pkcs10',
  868. 'p12' => 'application/x-pkcs12',
  869. 'p7b' => 'application/x-pkcs7-certificates',
  870. 'p7c' => 'application/x-pkcs7-mime',
  871. 'p7m' => 'application/x-pkcs7-mime',
  872. 'p7r' => 'application/x-pkcs7-certreqresp',
  873. 'p7s' => 'application/x-pkcs7-signature',
  874. 'pbm' => 'image/x-portable-bitmap',
  875. 'pdf' => 'application/pdf',
  876. 'pfx' => 'application/x-pkcs12',
  877. 'pgm' => 'image/x-portable-graymap',
  878. 'pko' => 'application/ynd.ms-pkipko',
  879. 'pma' => 'application/x-perfmon',
  880. 'pmc' => 'application/x-perfmon',
  881. 'pml' => 'application/x-perfmon',
  882. 'pmr' => 'application/x-perfmon',
  883. 'pmw' => 'application/x-perfmon',
  884. 'pnm' => 'image/x-portable-anymap',
  885. 'pot' => 'application/vnd.ms-powerpoint',
  886. 'ppm' => 'image/x-portable-pixmap',
  887. 'pps' => 'application/vnd.ms-powerpoint',
  888. 'ppt' => 'application/vnd.ms-powerpoint',
  889. 'prf' => 'application/pics-rules',
  890. 'ps' => 'application/postscript',
  891. 'pub' => 'application/x-mspublisher',
  892. 'qt' => 'video/quicktime',
  893. 'ra' => 'audio/x-pn-realaudio',
  894. 'ram' => 'audio/x-pn-realaudio',
  895. 'ras' => 'image/x-cmu-raster',
  896. 'rgb' => 'image/x-rgb',
  897. 'rmi' => 'audio/mid',
  898. 'roff' => 'application/x-troff',
  899. 'rtf' => 'application/rtf',
  900. 'rtx' => 'text/richtext',
  901. 'scd' => 'application/x-msschedule',
  902. 'sct' => 'text/scriptlet',
  903. 'setpay' => 'application/set-payment-initiation',
  904. 'setreg' => 'application/set-registration-initiation',
  905. 'sh' => 'application/x-sh',
  906. 'shar' => 'application/x-shar',
  907. 'sit' => 'application/x-stuffit',
  908. 'snd' => 'audio/basic',
  909. 'spc' => 'application/x-pkcs7-certificates',
  910. 'spl' => 'application/futuresplash',
  911. 'src' => 'application/x-wais-source',
  912. 'sst' => 'application/vnd.ms-pkicertstore',
  913. 'stl' => 'application/vnd.ms-pkistl',
  914. 'stm' => 'text/html',
  915. 'svg' => "image/svg+xml",
  916. 'sv4cpio' => 'application/x-sv4cpio',
  917. 'sv4crc' => 'application/x-sv4crc',
  918. 't' => 'application/x-troff',
  919. 'tar' => 'application/x-tar',
  920. 'tcl' => 'application/x-tcl',
  921. 'tex' => 'application/x-tex',
  922. 'texi' => 'application/x-texinfo',
  923. 'texinfo' => 'application/x-texinfo',
  924. 'tgz' => 'application/x-compressed',
  925. 'tif' => 'image/tiff',
  926. 'tiff' => 'image/tiff',
  927. 'tr' => 'application/x-troff',
  928. 'trm' => 'application/x-msterminal',
  929. 'tsv' => 'text/tab-separated-values',
  930. 'txt' => 'text/plain',
  931. 'uls' => 'text/iuls',
  932. 'ustar' => 'application/x-ustar',
  933. 'vcf' => 'text/x-vcard',
  934. 'vrml' => 'x-world/x-vrml',
  935. 'wav' => 'audio/x-wav',
  936. 'wcm' => 'application/vnd.ms-works',
  937. 'wdb' => 'application/vnd.ms-works',
  938. 'wks' => 'application/vnd.ms-works',
  939. 'wmf' => 'application/x-msmetafile',
  940. 'wps' => 'application/vnd.ms-works',
  941. 'wri' => 'application/x-mswrite',
  942. 'wrl' => 'x-world/x-vrml',
  943. 'wrz' => 'x-world/x-vrml',
  944. 'xaf' => 'x-world/x-vrml',
  945. 'xbm' => 'image/x-xbitmap',
  946. 'xla' => 'application/vnd.ms-excel',
  947. 'xlc' => 'application/vnd.ms-excel',
  948. 'xlm' => 'application/vnd.ms-excel',
  949. 'xls' => 'application/vnd.ms-excel',
  950. 'xlt' => 'application/vnd.ms-excel',
  951. 'xlw' => 'application/vnd.ms-excel',
  952. 'xof' => 'x-world/x-vrml',
  953. 'xpm' => 'image/x-xpixmap',
  954. 'xwd' => 'image/x-xwindowdump',
  955. 'z' => 'application/x-compress',
  956. 'zip' => 'application/zip',
  957. );
  958. $ext = pathinfo($filename, PATHINFO_EXTENSION);
  959. return isset($mime_types[$ext]) ? $mime_types[$ext] : $default;
  960. }
  961. /**
  962. * Filter Attributes
  963. *
  964. * Filters tag attributes for consistency and safety
  965. *
  966. * @access public
  967. * @param string
  968. * @return string
  969. */
  970. function _filter_attributes($str) {
  971. $out = '';
  972. if (preg_match_all('#\s*[a-z\-]+\s*=\s*(\042|\047)([^\\1]*?)\\1#is', $str, $matches))
  973. {
  974. foreach ($matches[0] as $match)
  975. {
  976. $out .= preg_replace("#/\*.*?\*/#s", '', $match);
  977. }
  978. }
  979. return $out;
  980. }
  981. /**
  982. * JS Link Removal
  983. *
  984. * Callback function for xss_clean() to sanitize links
  985. * This limits the PCRE backtracks, making it more performance friendly
  986. * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
  987. * PHP 5.2+ on link-heavy strings
  988. *
  989. * @param array
  990. * @return string
  991. */
  992. function _js_link_removal($match) {
  993. $attributes = _filter_attributes(str_replace(array('<', '>'), '', $match[1]));
  994. return str_replace($match[1], preg_replace("#href=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si", "", $attributes), $match[0]);
  995. }
  996. /**
  997. * JS Image Removal
  998. *
  999. * Callback function for xss_clean() to sanitize image tags
  1000. * This limits the PCRE backtracks, making it more performance friendly
  1001. * and prevents PREG_BACKTRACK_LIMIT_ERROR from being triggered in
  1002. * PHP 5.2+ on image tag heavy strings
  1003. *
  1004. * @param array
  1005. * @return string
  1006. */
  1007. function _js_img_removal($match) {
  1008. $attributes = _filter_attributes(str_replace(array('<', '>'), '', $match[1]));
  1009. return str_replace($match[1], preg_replace("#src=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\s*,)#si", "", $attributes), $match[0]);
  1010. }
  1011. /**
  1012. * Sanitize Naughty HTML
  1013. *
  1014. * Callback function for xss_clean() to remove naughty HTML elements
  1015. *
  1016. * @param array
  1017. * @return string
  1018. */
  1019. function _sanitize_naughty_html($matches) {
  1020. // encode opening brace
  1021. $str = '&lt;'.$matches[1].$matches[2].$matches[3];
  1022. // encode captured opening or closing brace to prevent recursive vectors
  1023. $str .= str_replace(array('>', '<'), array('&gt;', '&lt;'), $matches[4]);
  1024. return $str;
  1025. }
  1026. /**
  1027. * Compact Exploded Words
  1028. *
  1029. * Callback function for xss_clean() to remove whitespace from
  1030. * things like j a v a s c r i p t
  1031. *
  1032. * @param type
  1033. * @return type
  1034. */
  1035. function _compact_exploded_words($matches) {
  1036. return preg_replace('/\s+/s', '', $matches[1]).$matches[2];
  1037. }
  1038. /**
  1039. * Attribute Conversion
  1040. *
  1041. * Used as a callback for XSS Clean
  1042. *
  1043. * @param array
  1044. * @return string
  1045. */
  1046. function _convert_attribute($match) {
  1047. return str_replace(array('>', '<', '\\'), array('&gt;', '&lt;', '\\\\'), $match[0]);
  1048. }
  1049. // --------------------------------------------------------------------
  1050. /**
  1051. * HTML Entity Decode Callback
  1052. *
  1053. * Used as a callback for XSS Clean
  1054. *
  1055. * @param array
  1056. * @return string
  1057. */
  1058. function _html_entity_decode_callback($match) {
  1059. return html_entity_decode($match[0]);
  1060. }
  1061. /**
  1062. * Remove Invisible Characters
  1063. *
  1064. * Used as a callback for XSS Clean
  1065. *
  1066. * This prevents sandwiching null characters
  1067. * between ascii characters, like Java\0script.
  1068. *
  1069. * @param string
  1070. * @return string
  1071. */
  1072. function _remove_invisible_characters($str) {
  1073. static $non_displayables;
  1074. if ( ! isset($non_displayables))
  1075. {
  1076. // every control character except newline (dec 10), carriage return (dec 13), and horizontal tab (dec 09),
  1077. $non_displayables = array(
  1078. '/%0[0-8bcef]/', // url encoded 00-08, 11, 12, 14, 15
  1079. '/%1[0-9a-f]/', // url encoded 16-31
  1080. '/[\x00-\x08]/', // 00-08
  1081. '/\x0b/', '/\x0c/', // 11, 12
  1082. '/[\x0e-\x1f]/' // 14-31
  1083. );
  1084. }
  1085. do
  1086. {
  1087. $cleaned = $str;
  1088. $str = preg_replace($non_displayables, '', $str);
  1089. }
  1090. while ($cleaned != $str);
  1091. return $str;
  1092. }
  1093. /**
  1094. * Random Hash for protecting URLs
  1095. *
  1096. * @access public
  1097. * @return string
  1098. */
  1099. function xss_hash() {
  1100. if (phpversion() >= 4.2)
  1101. mt_srand();
  1102. else
  1103. mt_srand(hexdec(substr(md5(microtime()), -8)) & 0x7fffffff);
  1104. $output = md5(time() + mt_rand(0, 1999999999));
  1105. return $output;
  1106. }
  1107. /**
  1108. * XSS Clean
  1109. *
  1110. * Sanitizes data so that Cross Site Scripting Hacks can be
  1111. * prevented. This function does a fair amount of work but
  1112. * it is extremely thorough, designed to prevent even the
  1113. * most obscure XSS attempts. Nothing is ever 100% foolproof,
  1114. * of course, but I haven't been able to get anything passed
  1115. * the filter.
  1116. *
  1117. * Note: This function should only be used to deal with data
  1118. * upon submission. It's not something that should
  1119. * be used for general runtime processing.
  1120. *
  1121. * This function was based in part on some code and ideas I
  1122. * got from Bitflux: http://blog.bitflux.ch/wiki/XSS_Prevention
  1123. *
  1124. * To help develop this script I used this great list of
  1125. * vulnerabilities along with a few other hacks I've
  1126. * harvested from examining vulnerabilities in other programs:
  1127. * http://ha.ckers.org/xss.html
  1128. *
  1129. * @param string
  1130. * @return string
  1131. */
  1132. function xss_clean($str, $is_image = FALSE) {
  1133. global $never_allowed_str;
  1134. global $never_allowed_regex;
  1135. $xss_hash = xss_hash();
  1136. /*
  1137. * Is the string an array?
  1138. */
  1139. if (is_array($str)) {
  1140. while (list($key) = each($str)) {
  1141. $str[$key] = xss_clean($str[$key]);
  1142. }
  1143. return $str;
  1144. }
  1145. /*
  1146. * Remove Invisible Characters
  1147. */
  1148. $str = _remove_invisible_characters($str);
  1149. /*
  1150. * Protect GET variables in URLs
  1151. */
  1152. // 901119URL5918AMP18930PROTECT8198
  1153. $str = preg_replace('|\&([a-z\_0-9]+)\=([a-z\_0-9]+)|i', $xss_hash."\\1=\\2", $str);
  1154. /*
  1155. * Validate standard character entities
  1156. *
  1157. * Add a semicolon if missing. We do this to enable
  1158. * the conversion of entities to ASCII later.
  1159. *
  1160. */
  1161. $str = preg_replace('#(&\#?[0-9a-z]{2,})([\x00-\x20])*;?#i', "\\1;\\2", $str);
  1162. /*
  1163. * Validate UTF16 two byte encoding (x00)
  1164. *
  1165. * Just as above, adds a semicolon if missing.
  1166. *
  1167. */
  1168. $str = preg_replace('#(&\#x?)([0-9A-F]+);?#i',"\\1\\2;",$str);
  1169. /*
  1170. * Un-Protect GET variables in URLs
  1171. */
  1172. $str = str_replace($xss_hash, '&', $str);
  1173. /*
  1174. * URL Decode
  1175. *
  1176. * Just in case stuff like this is submitted:
  1177. *
  1178. * <a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
  1179. *
  1180. * Note: Use rawurldecode() so it does not remove plus signs
  1181. *
  1182. */
  1183. $str = rawurldecode($str);
  1184. /*
  1185. * Convert character entities to ASCII
  1186. *
  1187. * This permits our tests below to work reliably.
  1188. * We only convert entities that are within tags since
  1189. * these are the ones that will pose security problems.
  1190. *
  1191. */
  1192. $str = preg_replace_callback("/[a-z]+=([\'\"]).*?\\1/si", _convert_attribute, $str);
  1193. $str = preg_replace_callback("/<\w+.*?(?=>|<|$)/si", _html_entity_decode_callback, $str);
  1194. /*
  1195. * Remove Invisible Characters Again!
  1196. */
  1197. $str = _remove_invisible_characters($str);
  1198. /*
  1199. * Convert all tabs to spaces
  1200. *
  1201. * This prevents strings like this: ja vascript
  1202. * NOTE: we deal with spaces between characters later.
  1203. * NOTE: preg_replace was found to be amazingly slow here on large blocks of data,
  1204. * so we use str_replace.
  1205. *
  1206. */
  1207. if (strpos($str, "\t") !== FALSE) {
  1208. $str = str_replace("\t", ' ', $str);
  1209. }
  1210. /*
  1211. * Capture converted string for later comparison
  1212. */
  1213. $converted_string = $str;
  1214. /*
  1215. * Not Allowed Under Any Conditions
  1216. */
  1217. foreach ($never_allowed_str as $key => $val) {
  1218. $str = str_replace($key, $val, $str);
  1219. }
  1220. foreach ($never_allowed_regex as $key => $val) {
  1221. $str = preg_replace("#".$key."#i", $val, $str);
  1222. }
  1223. /*
  1224. * Makes PHP tags safe
  1225. *
  1226. * Note: XML tags are inadvertently replaced too:
  1227. *
  1228. * <?xml
  1229. *
  1230. * But it doesn't seem to pose a problem.
  1231. *
  1232. */
  1233. if ($is_image === TRUE) {
  1234. // Images have a tendency to have the PHP short opening and closing tags every so often
  1235. // so we skip those and only do the long opening tags.
  1236. $str = str_replace(array('<?php', '<?PHP'), array('&lt;?php', '&lt;?PHP'), $str);
  1237. } else {
  1238. $str = str_replace(array('<?php', '<?PHP', '<?', '?'.'>'), array('&lt;?php', '&lt;?PHP', '&lt;?', '?&gt;'), $str);
  1239. }
  1240. /*
  1241. * Compact any exploded words
  1242. *
  1243. * This corrects words like: j a v a s c r i p t
  1244. * These words are compacted back to their correct state.
  1245. *
  1246. */
  1247. $words = array('javascript', 'expression', 'vbscript', 'script', 'applet', 'alert', 'document', 'write', 'cookie', 'window');
  1248. foreach ($words as $word) {
  1249. $temp = '';
  1250. for ($i = 0, $wordlen = strlen($word); $i < $wordlen; $i++) {
  1251. $temp .= substr($word, $i, 1)."\s*";
  1252. }
  1253. // We only want to do this when it is followed by a non-word character
  1254. // That way valid stuff like "dealer to" does not become "dealerto"
  1255. $str = preg_replace_callback('#('.substr($temp, 0, -3).')(\W)#is', _compact_exploded_words, $str);
  1256. }
  1257. /*
  1258. * Remove disallowed Javascript in links or img tags
  1259. * We used to do some version comparisons and use of stripos for PHP5, but it is dog slow compared
  1260. * to these simplified non-capturing preg_match(), especially if the pattern exists in the string
  1261. */
  1262. do {
  1263. $original = $str;
  1264. if (preg_match("/<a/i", $str)) {
  1265. $str = preg_replace_callback("#<a\s+([^>]*?)(>|$)#si", _js_link_removal, $str);
  1266. }
  1267. if (preg_match("/<img/i", $str)) {
  1268. $str = preg_replace_callback("#<img\s+([^>]*?)(\s?/?>|$)#si", _js_img_removal, $str);
  1269. }
  1270. if (preg_match("/script/i", $str) OR preg_match("/xss/i", $str)) {
  1271. $str = preg_replace("#<(/*)(script|xss)(.*?)\>#si", '[removed]', $str);
  1272. }
  1273. }
  1274. while($original != $str);
  1275. unset($original);
  1276. /*
  1277. * Remove JavaScript Event Handlers
  1278. *
  1279. * Note: This code is a little blunt. It removes
  1280. * the event handler and anything up to the closing >,
  1281. * but it's unlikely to be a problem.
  1282. *
  1283. */
  1284. $event_handlers = array('[^a-z_\-]on\w*','xmlns');
  1285. if ($is_image === TRUE) {
  1286. /*
  1287. * Adobe Photoshop puts XML metadata into JFIF images, including namespacing,
  1288. * so we have to allow this for images. -Paul
  1289. */
  1290. unset($event_handlers[array_search('xmlns', $event_handlers)]);
  1291. }
  1292. $str = preg_replace("#<([^><]+?)(".implode('|', $event_handlers).")(\s*=\s*[^><]*)([><]*)#i", "<\\1\\4", $str);
  1293. /*
  1294. * Sanitize naughty HTML elements
  1295. *
  1296. * If a tag containing any of the words in the list
  1297. * below is found, the tag gets converted to entities.
  1298. *
  1299. * So this: <blink>
  1300. * Becomes: &lt;blink&gt;
  1301. *
  1302. */
  1303. $naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss';
  1304. $str = preg_replace_callback('#<(/*\s*)('.$naughty.')([^><]*)([><]*)#is', _sanitize_naughty_html, $str);
  1305. /*
  1306. * Sanitize naughty scripting elements
  1307. *
  1308. * Similar to above, only instead of looking for
  1309. * tags it looks for PHP and JavaScript commands
  1310. * that are disallowed. Rather than removing the
  1311. * code, it simply converts the parenthesis to entities
  1312. * rendering the code un-executable.
  1313. *
  1314. * For example: eval('some code')
  1315. * Becomes: eval&#40;'some code'&#41;
  1316. *
  1317. */
  1318. $str = preg_replace('#(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\s*)\((.*?)\)#si', "\\1\\2&#40;\\3&#41;", $str);
  1319. /*
  1320. * Final clean up
  1321. *
  1322. * This adds a bit of extra precaution in case
  1323. * something got through the above filters
  1324. *
  1325. */
  1326. foreach ($never_allowed_str as $key => $val) {
  1327. $str = str_replace($key, $val, $str);
  1328. }
  1329. foreach ($never_allowed_regex as $key => $val) {
  1330. $str = preg_replace("#".$key."#i", $val, $str);
  1331. }
  1332. /*
  1333. * Images are Handled in a Special Way
  1334. * - Essentially, we want to know that after all of the character conversion is done whether
  1335. * any unwanted, likely XSS, code was found. If not, we return TRUE, as the image is clean.
  1336. * However, if the string post-conversion does not matched the string post-removal of XSS,
  1337. * then it fails, as there was unwanted XSS code found and removed/changed during processing.
  1338. */
  1339. if ($is_image === TRUE) {
  1340. if ($str == $converted_string) {
  1341. return TRUE;
  1342. } else {
  1343. return FALSE;
  1344. }
  1345. }
  1346. return $str;
  1347. }
  1348. /**
  1349. * Simple variable dump
  1350. *
  1351. * @param mixed $var
  1352. * @return void
  1353. * @author j4kp07
  1354. */
  1355. function dump($var = NULL)
  1356. {
  1357. echo '<pre>';
  1358. print_r($var);
  1359. exit();
  1360. }
  1361. /**
  1362. * var_dump for php debugging
  1363. *
  1364. * Source: http://us2.php.net/manual/en/function.var-dump.php
  1365. *
  1366. * @author j4kp07
  1367. */
  1368. function _vardump(&$var, $info = FALSE)
  1369. {
  1370. $scope = false;
  1371. $prefix = 'unique';
  1372. $suffix = 'value';
  1373. if($scope) $vals = $scope;
  1374. else $vals = $GLOBALS;
  1375. $old = $var;
  1376. $var = $new = $prefix.rand().$suffix; $vname = FALSE;
  1377. foreach($vals as $key => $val) if($val === $new) $vname = $key;
  1378. $var = $old;
  1379. echo "<pre style='margin: 0px 0px 10px 0px; display: block; background: white; color: black; font-family: Verdana; border: 1px solid #cccccc; padding: 5px; font-size: 10px; line-height: 13px;'>";
  1380. if($info != FALSE) echo "<b style='color: red;'>$info:</b><br>";
  1381. do_dump($var, '$'.$vname);
  1382. echo "</pre>";
  1383. exit();
  1384. }
  1385. /**
  1386. * Better UI than print_r or var_dump
  1387. *
  1388. * Source: http://us2.php.net/manual/en/function.var-dump.php
  1389. *
  1390. * @author j4kp07
  1391. */
  1392. function do_dump(&$var, $var_name = NULL, $indent = NULL, $reference = NULL)
  1393. {
  1394. $do_dump_indent = "<span style='color:#eeeeee;'>|</span> &nbsp;&nbsp; ";
  1395. $reference = $reference.$var_name;
  1396. $keyvar = 'the_do_dump_recursion_protection_scheme'; $keyname = 'referenced_object_name';
  1397. if (is_array($var) && isset($var[$keyvar]))
  1398. {
  1399. $real_var = &$var[$keyvar];
  1400. $real_name = &$var[$keyname];
  1401. $type = ucfirst(gettype($real_var));
  1402. echo "$indent$var_name <span style='color:#a2a2a2'>$type</span> = <span style='color:#e87800;'>&amp;$real_name</span><br>";
  1403. }
  1404. else
  1405. {
  1406. $var = array($keyvar => $var, $keyname => $reference);
  1407. $avar = &$var[$keyvar];
  1408. $type = ucfirst(gettype($avar));
  1409. if($type == "String") $type_color = "<span style='color:green'>";
  1410. elseif($type == "Integer") $type_color = "<span style='color:red'>";
  1411. elseif($type == "Double"){ $type_color = "<span style='color:#0099c5'>"; $type = "Float"; }
  1412. elseif($type == "Boolean") $type_color = "<span style='color:#92008d'>";
  1413. elseif($type == "NULL") $type_color = "<span style='color:black'>";
  1414. if(is_array($avar))
  1415. {
  1416. $count = count($avar);
  1417. echo "$indent" . ($var_name ? "$var_name => ":"") . "<span style='color:#a2a2a2'>$type ($count)</span><br>$indent(<br>";
  1418. $keys = array_keys($avar);
  1419. foreach($keys as $name)
  1420. {
  1421. $value = &$avar[$name];
  1422. do_dump($value, "['$name']", $indent.$do_dump_indent, $reference);
  1423. }
  1424. echo "$indent)<br>";
  1425. }
  1426. elseif(is_object($avar))
  1427. {
  1428. echo "$indent$var_name <span style='color:#a2a2a2'>$type</span><br>$indent(<br>";
  1429. foreach($avar as $name=>$value) do_dump($value, "$name", $indent.$do_dump_indent, $reference);
  1430. echo "$indent)<br>";
  1431. }
  1432. elseif(is_int($avar)) echo "$indent$var_name = <span style='color:#a2a2a2'>$type(".strlen($avar).")</span> $type_color$avar</span><br>";
  1433. elseif(is_string($avar)) echo "$indent$var_name = <span style='color:#a2a2a2'>$type(".strlen($avar).")</span> $type_color\"$avar\"</span><br>";
  1434. elseif(is_float($avar)) echo "$indent$var_name = <span style='color:#a2a2a2'>$type(".strlen($avar).")</span> $type_color$avar</span><br>";
  1435. elseif(is_bool($avar)) echo "$indent$var_name = <span style='color:#a2a2a2'>$type(".strlen($avar).")</span> $type_color".($avar == 1 ? "TRUE":"FALSE")."</span><br>";
  1436. elseif(is_null($avar)) echo "$indent$var_name = <span style='color:#a2a2a2'>$type(".strlen($avar).")</span> {$type_color}NULL</span><br>";
  1437. else echo "$indent$var_name = <span style='color:#a2a2a2'>$type(".strlen($avar).")</span> $avar<br>";
  1438. $var = $var[$keyvar];
  1439. }
  1440. }