PageRenderTime 139ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/system/functions.php

http://github.com/Cotonti/Cotonti
PHP | 5754 lines | 4227 code | 438 blank | 1089 comment | 709 complexity | 010ee4a0819b072e95cb57b8fa37606b MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * Main function library.
  4. *
  5. * @package API - Functions
  6. * @copyright (c) Cotonti Team
  7. * @license https://github.com/Cotonti/Cotonti/blob/master/License.txt
  8. */
  9. defined('COT_CODE') or die('Wrong URL');
  10. // System requirements check
  11. if (!defined('COT_INSTALL'))
  12. {
  13. (function_exists('version_compare') && version_compare(PHP_VERSION, '5.3.3', '>=')) or die('Cotonti system requirements: PHP 5.3.3 or above.'); // TODO: Need translate
  14. extension_loaded('mbstring') or die('Cotonti system requirements: mbstring PHP extension must be loaded.'); // TODO: Need translate
  15. }
  16. // Group constants
  17. define('COT_GROUP_DEFAULT', 0);
  18. define('COT_GROUP_GUESTS', 1);
  19. define('COT_GROUP_INACTIVE', 2);
  20. define('COT_GROUP_BANNED', 3);
  21. define('COT_GROUP_MEMBERS', 4);
  22. define('COT_GROUP_SUPERADMINS', 5);
  23. define('COT_GROUP_MODERATORS', 6);
  24. /* ======== Pre-sets ========= */
  25. $out = array();
  26. $plu = array();
  27. $sys = array();
  28. $usr = array();
  29. $env = array();
  30. $L = array();
  31. $R = array();
  32. $i = explode(' ', microtime());
  33. $sys['starttime'] = $i[1] + $i[0];
  34. $cfg['version'] = '0.9.19';
  35. $cfg['dbversion'] = '0.9.19';
  36. // Set default file permissions if not present in config
  37. if (!isset($cfg['file_perms']))
  38. {
  39. $cfg['file_perms'] = 0664;
  40. }
  41. if (!isset($cfg['dir_perms']))
  42. {
  43. $cfg['dir_perms'] = 0777;
  44. }
  45. /**
  46. * Registry for captcha functions
  47. */
  48. $cot_captcha = array();
  49. /**
  50. * Registry for extra fields
  51. * @var array
  52. */
  53. $cot_extrafields = null;
  54. /**
  55. * Registry for hash functions
  56. */
  57. $cot_hash_funcs = array('md5', 'sha1', 'sha256');
  58. /**
  59. * Array of custom cot_import() filter callbacks
  60. */
  61. $cot_import_filters = array();
  62. /**
  63. * Custom e-mail send callbacks
  64. */
  65. $cot_mail_senders = array();
  66. /**
  67. * Custom parser functions registry
  68. */
  69. $cot_parsers = array();
  70. /**
  71. * Parameters to be automatically appended to all URLs if present
  72. */
  73. $cot_url_appendix = array();
  74. /**
  75. * Structure tree
  76. * @var array
  77. */
  78. $structure = array();
  79. /**
  80. * Facade class to access key Cotonti globals regardless of scope
  81. */
  82. class cot
  83. {
  84. /**
  85. * Cotonti cache
  86. * @var Cache
  87. */
  88. public static $cache;
  89. /**
  90. * Cotonti configuration
  91. * @var array
  92. */
  93. public static $cfg;
  94. /**
  95. * Database connection
  96. * @var CotDB
  97. */
  98. public static $db;
  99. /**
  100. * Database table name prefix
  101. */
  102. public static $db_x;
  103. /**
  104. * Environment settings
  105. * @var array
  106. */
  107. public static $env;
  108. /**
  109. * Extra fields
  110. * @var array
  111. */
  112. public static $extrafields;
  113. /**
  114. * Language strings
  115. * @var array
  116. */
  117. public static $L;
  118. /**
  119. * Pre-rendered output strings
  120. * @var array
  121. */
  122. public static $out;
  123. /**
  124. * Resource strings
  125. * @var array
  126. */
  127. public static $R;
  128. /**
  129. * Structure tree and properties array
  130. * @var array
  131. */
  132. public static $structure;
  133. /**
  134. * Temporary system variables
  135. * @var array
  136. */
  137. public static $sys;
  138. /**
  139. * Current user object
  140. * @var array
  141. */
  142. public static $usr;
  143. /**
  144. * Initializes static members. Call this function once all globals are defined.
  145. */
  146. public static function init()
  147. {
  148. global $cache, $cfg, $cot_extrafields, $db, $db_x, $env, $L, $out, $R, $structure, $sys, $usr;
  149. // Todo fill some variables with defoult values
  150. self::$cache =& $cache;
  151. self::$cfg =& $cfg;
  152. self::$db =& $db;
  153. self::$db_x =& $db_x;
  154. self::$env =& $env;
  155. self::$extrafields =& $cot_extrafields;
  156. self::$L =& $L;
  157. self::$out =& $out;
  158. self::$R =& $R;
  159. self::$structure =& $structure;
  160. self::$sys =& $sys;
  161. self::$usr =& $usr;
  162. // Register core DB tables
  163. // On the first step of installer it is not initialized yet
  164. if ( !(empty($db) && isset($env['location']) && $env['location'] == 'install' )) {
  165. $db->registerTable('auth');
  166. $db->registerTable('cache');
  167. $db->registerTable('cache_bindings');
  168. $db->registerTable('core');
  169. $db->registerTable('config');
  170. $db->registerTable('groups');
  171. $db->registerTable('groups_users');
  172. $db->registerTable('logger');
  173. $db->registerTable('online');
  174. $db->registerTable('extra_fields');
  175. $db->registerTable('plugins');
  176. $db->registerTable('structure');
  177. $db->registerTable('updates');
  178. $db->registerTable('users');
  179. }
  180. // Fill some variables with default values
  181. // May be isset() is not needed
  182. if(!isset(self::$out['head'])) self::$out['head'] = '';
  183. if(!isset(self::$out['subtitle'])) self::$out['subtitle'] = '';
  184. if(!isset(self::$env['ext'])) self::$env['ext'] = null;
  185. }
  186. }
  187. /*
  188. * =========================== System Functions ===============================
  189. */
  190. /**
  191. * Strips everything but alphanumeric, hyphens and underscores
  192. *
  193. * @param string $text Input
  194. * @return string
  195. */
  196. function cot_alphaonly($text)
  197. {
  198. return(preg_replace('/[^a-zA-Z0-9\-_]/', '', $text));
  199. }
  200. /**
  201. * Generic autoloader function to be used with spl_autoload_register
  202. *
  203. * @todo PSR-4 autoloader
  204. * @param string $class Class name
  205. */
  206. function cot_autoload($class)
  207. {
  208. global $cfg, $env;
  209. $type = isset(cot::$env['type']) ? cot::$env['type'] : null;
  210. if ($type == 'module') {
  211. $paths[] = "{$cfg['modules_dir']}/{$env['ext']}/classes/$class.php";
  212. } elseif ($type == 'plug') {
  213. $paths[] = "{$cfg['plugins_dir']}/{$env['ext']}/classes/$class.php";
  214. }
  215. $paths[] = "{$cfg['system_dir']}/classes/$class.php";
  216. foreach ($paths as $path) {
  217. if (file_exists($path)) {
  218. require $path;
  219. return;
  220. }
  221. }
  222. }
  223. /**
  224. * Truncates a string
  225. *
  226. * @param string $res Source string
  227. * @param int $l Length
  228. * @return string
  229. */
  230. function cot_cutstring($res, $l)
  231. {
  232. if (mb_strlen($res)>$l)
  233. {
  234. $res = mb_substr($res, 0, ($l-3)).'...';
  235. }
  236. return $res;
  237. }
  238. /**
  239. * Returns name part of the caller file. Use this in plugins to detect from which file
  240. * current hook part was included. Example:
  241. * <code>
  242. * if (cot_get_caller() == 'users.details')
  243. * {
  244. * // We are called from users.details
  245. * }
  246. * else if (cot_get_caller() == 'header')
  247. * {
  248. * // We are called from header
  249. * }
  250. * </code>
  251. * @return string Caller file basename without .php suffix on success, 'unknown' or error
  252. */
  253. function cot_get_caller()
  254. {
  255. $bt = debug_backtrace();
  256. if (isset($bt[1]) && in_array($bt[1]['function'], array('include', 'require_once', 'require', 'include_once')))
  257. {
  258. return preg_replace('#\.php$#', '', basename($bt[1]['file']));
  259. }
  260. else
  261. {
  262. return 'unknown';
  263. }
  264. }
  265. /**
  266. * Returns a list of plugins registered for a hook
  267. *
  268. * @param string $hook Hook name
  269. * @param string $cond Permissions
  270. * @return array
  271. * @global Cache $cache
  272. */
  273. function cot_getextplugins($hook, $cond='R')
  274. {
  275. global $cot_plugins, $cache, $cfg, $cot_hooks_fired;
  276. if ($cfg['debug_mode'])
  277. {
  278. $cot_hooks_fired[] = $hook;
  279. }
  280. $extplugins = array();
  281. if (isset($cot_plugins[$hook]) && is_array($cot_plugins[$hook]))
  282. {
  283. foreach($cot_plugins[$hook] as $k)
  284. {
  285. if ($k['pl_module'])
  286. {
  287. $dir = $cfg['modules_dir'];
  288. $cat = $k['pl_code'];
  289. $opt = 'a';
  290. }
  291. else
  292. {
  293. $dir = $cfg['plugins_dir'];
  294. $cat = 'plug';
  295. $opt = $k['pl_code'];
  296. }
  297. if (cot_auth($cat, $opt, $cond))
  298. {
  299. $extplugins[] = $dir . '/' . $k['pl_file'];
  300. }
  301. }
  302. }
  303. // Trigger cache handlers
  304. $cache && $cache->trigger($hook);
  305. return $extplugins;
  306. }
  307. /**
  308. * Imports data from the outer world
  309. *
  310. * @param string $name Variable name
  311. * @param string $source Source type: G/GET, P/POST, C/COOKIE, R/REQUEST, PUT, DELETE or D/DIRECT (variable filtering)
  312. * @param string $filter Filter type
  313. * @param int $maxlen Length limit
  314. * @param bool $dieonerror Die with fatal error on wrong input
  315. * @param bool $buffer Try to load from input buffer (previously submitted) if current value is empty
  316. * @return mixed
  317. */
  318. function cot_import($name, $source, $filter, $maxlen = 0, $dieonerror = false, $buffer = false)
  319. {
  320. global $cot_import_filters, $_PUT, $_PATCH, $_DELETE;
  321. if(isset($_SERVER['REQUEST_METHOD']))
  322. {
  323. if ($_SERVER['REQUEST_METHOD'] == 'PUT' && is_null($_PUT))
  324. {
  325. parse_str(file_get_contents('php://input'), $_PUT);
  326. }
  327. elseif ($_SERVER['REQUEST_METHOD'] == 'PATCH' && is_null($_PATCH))
  328. {
  329. parse_str(file_get_contents('php://input'), $_PATCH);
  330. }
  331. elseif ($_SERVER['REQUEST_METHOD'] == 'DELETE' && is_null($_DELETE))
  332. {
  333. parse_str(file_get_contents('php://input'), $_DELETE);
  334. }
  335. }
  336. $v = NULL;
  337. switch($source)
  338. {
  339. case 'G':
  340. case 'GET':
  341. $v = (isset($_GET[$name])) ? $_GET[$name] : NULL;
  342. $log = TRUE;
  343. break;
  344. case 'P':
  345. case 'POST':
  346. $v = (isset($_POST[$name])) ? $_POST[$name] : NULL;
  347. $log = TRUE;
  348. break;
  349. case 'PUT':
  350. $v = (isset($_PUT[$name])) ? $_PUT[$name] : NULL;
  351. $log = TRUE;
  352. break;
  353. case 'PATCH':
  354. $v = (isset($_PATCH[$name])) ? $_PATCH[$name] : NULL;
  355. $log = TRUE;
  356. break;
  357. case 'DELETE':
  358. $v = (isset($_DELETE[$name])) ? $_DELETE[$name] : NULL;
  359. $log = TRUE;
  360. break;
  361. case 'R':
  362. case 'REQUEST':
  363. $v = (isset($_REQUEST[$name])) ? $_REQUEST[$name] : NULL;
  364. $log = TRUE;
  365. break;
  366. case 'C':
  367. case 'COOKIE':
  368. $v = (isset($_COOKIE[$name])) ? $_COOKIE[$name] : NULL;
  369. $log = TRUE;
  370. break;
  371. case 'D':
  372. case 'DIRECT':
  373. $v = $name;
  374. $log = FALSE;
  375. break;
  376. default:
  377. cot_diefatal('Unknown source for a variable : <br />Name = '.$name.'<br />Source = '.$source.' ? (must be G, P, C or D)');
  378. break;
  379. }
  380. if (is_array($v))
  381. {
  382. if ($filter == 'NOC') $filter = 'ARR';
  383. if ($filter != 'ARR') return null;
  384. }
  385. else
  386. {
  387. if ($filter == 'ARR') return array();
  388. }
  389. if (MQGPC && ($source=='G' || $source=='P' || $source=='C') && $v != NULL && $filter != 'ARR')
  390. {
  391. $v = stripslashes($v);
  392. }
  393. if (($v === '' || $v === NULL || $filter == 'ARR') && $buffer)
  394. {
  395. $v = cot_import_buffered($name, $v, null);
  396. return $v;
  397. }
  398. if ($v === null)
  399. {
  400. return null;
  401. }
  402. if ($maxlen>0)
  403. {
  404. $v = mb_substr($v, 0, $maxlen);
  405. }
  406. $pass = FALSE;
  407. $defret = NULL;
  408. // Custom filter support
  409. if (!empty($cot_import_filters[$filter]) && is_array($cot_import_filters[$filter])) {
  410. foreach ($cot_import_filters[$filter] as $func) {
  411. $v = $func($v, $name);
  412. }
  413. return $v;
  414. }
  415. switch($filter) {
  416. case 'INT':
  417. if (is_numeric($v) && floor($v)==$v) {
  418. $pass = TRUE;
  419. $v = (int) $v;
  420. }
  421. break;
  422. case 'NUM':
  423. if (is_numeric($v)) {
  424. $pass = TRUE;
  425. $v = (float) $v;
  426. }
  427. break;
  428. case 'TXT':
  429. $v = trim($v);
  430. if (mb_strpos($v, '<')===FALSE) {
  431. $pass = TRUE;
  432. } else {
  433. $defret = str_replace('<', '&lt;', $v);
  434. }
  435. break;
  436. case 'ALP':
  437. $v = trim($v);
  438. $f = cot_alphaonly($v);
  439. if ($v == $f)
  440. {
  441. $pass = TRUE;
  442. }
  443. else
  444. {
  445. $defret = $f;
  446. }
  447. break;
  448. case 'PSW':
  449. $v = trim($v);
  450. $f = preg_replace('#[\'"&<>]#', '', $v);
  451. $f = mb_substr($f, 0 ,32);
  452. if ($v == $f)
  453. {
  454. $pass = TRUE;
  455. }
  456. else
  457. {
  458. $defret = $f;
  459. }
  460. break;
  461. case 'HTM':
  462. $v = trim($v);
  463. $pass = TRUE;
  464. break;
  465. case 'ARR':
  466. $pass = TRUE;
  467. break;
  468. case 'BOL':
  469. if ($v == '1' || $v == 'on')
  470. {
  471. $pass = TRUE;
  472. $v = TRUE;
  473. }
  474. elseif ($v=='0' || $v=='off')
  475. {
  476. $pass = TRUE;
  477. $v = FALSE;
  478. }
  479. else
  480. {
  481. $defret = FALSE;
  482. }
  483. break;
  484. case 'NOC':
  485. $pass = TRUE;
  486. break;
  487. default:
  488. cot_diefatal('Unknown filter for a variable : <br />Var = '.$v.'<br />Filter = &quot;'.$filter.'&quot; ?');
  489. break;
  490. }
  491. if (!$pass || !in_array($filter, array('INT', 'NUM', 'BOL', 'ARR')))
  492. {
  493. $v = preg_replace('/(&#\d+)(?![\d;])/', '$1;', $v);
  494. }
  495. if ($pass)
  496. {
  497. return $v;
  498. }
  499. else
  500. {
  501. if ($log)
  502. {
  503. cot_log_import($source, $filter, $name, $v);
  504. }
  505. if ($dieonerror)
  506. {
  507. cot_diefatal('Wrong input.');
  508. }
  509. else
  510. {
  511. return $defret;
  512. }
  513. }
  514. return null;
  515. }
  516. /**
  517. * Imports data from the outer world by list of Variable names
  518. * Relies on `cot_import` function
  519. *
  520. * @param mixed $nameslist List of Variables names to import, can be:<br />
  521. * string 'name1, name2 , ...' - list of variable names comma separated.<br />
  522. * array('name1', 'name2', ...) - list of variable names only.
  523. * In that case $filter parameter must be specified.<br />
  524. * array('name1' => 'TYPE1', 'name2' => 'TYPE2' ,...) - list of variable names with their filter types
  525. * @param string $source Source type: G/GET, P/POST, C/COOKIE, R/REQUEST, PUT, DELETE or D/DIRECT (variable filtering)
  526. * @param array $origindata Array with origin data that will be extended with imported one
  527. * @param string $nameprefix Unified prefix for Variables names
  528. * @param string $filter Filter type, can be set as:<br />
  529. * string 'FLT' - single filter string for all Variables<br />
  530. * string 'FLT1, FLT2, ...' - comma separated string with filters corresponding to Variable names<br />
  531. * array('FLT1', 'FLT2', ...) - array of filters<br />
  532. * Overrides Filter types specified in $nameslist. If passed as list - number of Filters must be equal to count of
  533. * variables names in $nameslist.
  534. *
  535. * @param bool $arrayprefix Use $nameprefix for array fields
  536. * @param int $maxlen Length limit
  537. * @param bool $dieonerror Die with fatal error on wrong input
  538. * @param bool $buffer Try to load from input buffer (previously submitted) if current value is empty
  539. * @return boolean|array Returns combined array of data or FALSE if wrong parameters set
  540. */
  541. function cot_import_list($nameslist=array(), $source='P', $origindata=array(), $nameprefix='', $filter=null, $arrayprefix=false, $maxlen=0, $dieonerror=false, $buffer=false)
  542. {
  543. $direct = ($source == 'D' || $source == 'DIRECT');
  544. $filter = empty($filter) ? null : $filter;
  545. $nameslist = empty($nameslist) ? array() : $nameslist;
  546. $origindata = !is_array($origindata) ? array() : $origindata;
  547. if (!is_array($nameslist) && !empty($nameslist))
  548. {
  549. $nameslist = array_map('trim', explode(',',$nameslist));
  550. }
  551. if (!is_array($filter) && strpos($filter, ',') !== false)
  552. {
  553. $filter = array_map('trim', explode(',',$filter));
  554. }
  555. elseif (!is_array($filter) && !is_null($filter))
  556. {
  557. $filter = array_fill(0,sizeof($direct && empty($nameslist) ? $origindata : $nameslist),$filter);
  558. }
  559. if (!$direct && sizeof($nameslist) == 0)
  560. {
  561. return false; // no proper name list
  562. }
  563. elseif (sizeof($nameslist) == 0)
  564. { // direct by origin
  565. if (is_null($filter)) return false;
  566. foreach ($origindata as $key => $value) {
  567. $origindata[$key] = cot_import($value, 'D', array_shift($filter), $maxlen, $dieonerror);
  568. }
  569. }
  570. else
  571. { // namelist exists
  572. $index = array_keys($nameslist);
  573. $index = array_pop($index);
  574. $types_not_defined = (is_numeric($index) && is_int($index));
  575. if ((is_array($filter) && sizeof($filter) != sizeof($nameslist))
  576. || ($types_not_defined && is_null($filter)))
  577. {
  578. return false; // can't rely on filter or no filter exists
  579. }
  580. elseif (is_array($filter))
  581. {
  582. $nameslist = array_combine($types_not_defined ? $nameslist : array_keys($nameslist), $filter);
  583. }
  584. foreach ($nameslist as $name => $filtertype) {
  585. $origindata[($arrayprefix) ? $nameprefix.$name : $name] = cot_import($direct ? $origindata[$nameprefix.$name] : $nameprefix.$name, $source, $filtertype, $maxlen, $dieonerror, $buffer);
  586. }
  587. }
  588. return $origindata;
  589. }
  590. /**
  591. * Imports data from the outer world as indexed array of records imported by cot_import_list.
  592. * Used to import table editing data as one array ordered by index (IDs) of table lines.
  593. *
  594. * @see cot_import_list() for parameters
  595. *
  596. * @return boolean|array Returns indexed array of data or FALSE if wrong parameters setted
  597. */
  598. function cot_import_tabledata($nameslist=array(), $source='P', $nameprefix='', $origindata=array(), $maxlen=0, $dieonerror=false, $buffer=false)
  599. {
  600. $imported_arrays = cot_import_list($nameslist, $source, $origindata, $nameprefix,'ARR', $maxlen, $dieonerror, $buffer);
  601. if (!$imported_arrays) return false;
  602. $result = array();
  603. $na_data = array();
  604. foreach ($imported_arrays as $name => $data)
  605. {
  606. if (!is_array($data))
  607. {
  608. $na_data[$name] = $data;
  609. unset($imported_arrays[$name]);
  610. }
  611. }
  612. foreach ($imported_arrays as $name => $data)
  613. {
  614. if (is_array($data))
  615. {
  616. foreach ($data as $index => $value)
  617. {
  618. $result[$index][$name] = $value;
  619. foreach ($na_data as $k => $v) {
  620. $result[$index][$k] = $v;
  621. }
  622. }
  623. }
  624. }
  625. return $result;
  626. }
  627. /**
  628. * Puts POST data into the cross-request buffer
  629. */
  630. function cot_import_buffer_save()
  631. {
  632. // Referer contains an original form link
  633. if (cot_url_check($_SERVER['HTTP_REFERER']))
  634. {
  635. // Extract the server-relative part
  636. $url = parse_url($_SERVER['HTTP_REFERER']);
  637. // Strip ajax param from the query
  638. $url['query'] = str_replace('&_ajax=1', '', $url['query']);
  639. $path = empty($url['query']) ? $url['path'] : $url['path'] . '?' . $url['query'];
  640. $hash = md5($path);
  641. // Save the buffer
  642. $_SESSION['cot_buffer'][$hash] = $_POST;
  643. }
  644. }
  645. /**
  646. * Attempts to fetch a buffered value for a variable previously imported
  647. * if the currently imported value is empty
  648. *
  649. * @param string $name Input name
  650. * @param mixed $value Currently imported value
  651. * @param mixed $null null import
  652. * @return mixed Input value or NULL if the variable is not in the buffer
  653. */
  654. function cot_import_buffered($name, $value, $null = '')
  655. {
  656. // Params hash for current form
  657. $uri = str_replace('&_ajax=1', '', $_SERVER['REQUEST_URI']);
  658. $hash = md5($uri);
  659. if ($value === '' || $value === null
  660. || isset($_SESSION['cot_buffer'][$hash][$name]) && !empty($_SESSION['cot_buffer'][$hash][$name]))
  661. {
  662. if (isset($_SESSION['cot_buffer'][$hash][$name]))
  663. {
  664. return $_SESSION['cot_buffer'][$hash][$name];
  665. }
  666. else
  667. {
  668. return $null;
  669. }
  670. }
  671. else
  672. {
  673. return $value;
  674. }
  675. }
  676. /**
  677. * Imports date stamp
  678. *
  679. * @param string $name Variable name preffix
  680. * @param bool $usertimezone Use user timezone
  681. * @param bool $returnarray Return Date Array
  682. * @param string $source Source type: P (POST), C (COOKIE) or D (variable filtering)
  683. * @return mixed
  684. */
  685. function cot_import_date($name, $usertimezone = true, $returnarray = false, $source = 'P')
  686. {
  687. if (function_exists('cot_import_date_custom'))
  688. {
  689. return cot_import_date_custom($name, $usertimezone, $returnarray, $source);
  690. }
  691. $result = NULL;
  692. /* === Hook === */
  693. foreach (cot_getextplugins('import.date') as $pl)
  694. {
  695. include $pl;
  696. }
  697. /* ===== */
  698. if($result !== NULL) return $result;
  699. //$name = preg_match('#^(\w+)\[(.*?)\]$#', $name, $mt) ? $mt[1] : $name;
  700. $date = cot_import($name, $source, 'ARR');
  701. $year = cot_import($date['year'], 'D', 'INT');
  702. $month = cot_import($date['month'], 'D', 'INT');
  703. $day = cot_import($date['day'], 'D', 'INT');
  704. $hour = cot_import($date['hour'], 'D', 'INT');
  705. $minute = cot_import($date['minute'], 'D', 'INT');
  706. if (count($date) > 0 && is_null($year) && is_null($month) && is_null($day) && is_null($hour) && is_null($minute))
  707. {
  708. // Datetime field is present in form but it is set to zero date (empty)
  709. return NULL;
  710. }
  711. if (($month && $day && $year) || ($day && $minute))
  712. {
  713. $timestamp = cot_mktime($hour, $minute, 0, $month, $day, $year);
  714. }
  715. else
  716. {
  717. $string = cot_import($date['string'], 'D', 'TXT');
  718. $format = cot_import($date['format'], 'D', 'TXT');
  719. if ($string && $format)
  720. {
  721. $timestamp = cot_date2stamp($string, $format);
  722. }
  723. else
  724. {
  725. return NULL;
  726. }
  727. }
  728. if ($usertimezone)
  729. {
  730. $timestamp -= cot::$usr['timezone'] * 3600;
  731. }
  732. if ($returnarray)
  733. {
  734. $result = array();
  735. $result['stamp'] = $timestamp;
  736. $result['year'] = (int)date('Y', $timestamp);
  737. $result['month'] = (int)date('m', $timestamp);
  738. $result['day'] = (int)date('d', $timestamp);
  739. $result['hour'] = (int)date('H', $timestamp);
  740. $result['minute'] = (int)date('i', $timestamp);
  741. return $result;
  742. }
  743. return $timestamp;
  744. }
  745. /**
  746. * Imports pagination indexes
  747. *
  748. * @param string $var_name URL parameter name, e.g. 'pg' or 'd'
  749. * @param int $max_items Max items per page
  750. * @return array Array containing 3 items: page number, database offset and argument for URLs
  751. */
  752. function cot_import_pagenav($var_name, $max_items = 0)
  753. {
  754. global $cfg;
  755. if($max_items <= 0)
  756. {
  757. $max_items = $cfg['maxrowsperpage'];
  758. }
  759. if($max_items <= 0)
  760. {
  761. throw new Exception('Invalid $max_items ('.$max_items.') for pagination.');
  762. }
  763. if ($cfg['easypagenav'])
  764. {
  765. $page = (int) cot_import($var_name, 'G', 'INT');
  766. if ($page < 0)
  767. {
  768. cot_die_message(404);
  769. }
  770. elseif ($page == 0)
  771. {
  772. $page = 1;
  773. }
  774. $offset = ($page - 1) * $max_items;
  775. $urlnum = $page <= 1 ? null : $page;
  776. }
  777. else
  778. {
  779. $offset = (int) cot_import($var_name, 'G', 'INT');
  780. if ($offset < 0)
  781. {
  782. cot_die_message(404);
  783. }
  784. if ($offset % $max_items != 0)
  785. {
  786. $offset -= $offset % $max_items;
  787. }
  788. $page = floor($offset / $max_items) + 1;
  789. $urlnum = $offset;
  790. $urlnum = ($urlnum > 0) ? $urlnum : null;
  791. }
  792. return array($page, $offset, $urlnum);
  793. }
  794. /**
  795. * Checks the email
  796. *
  797. * @param string $res input string
  798. * @return bool True if email valid
  799. */
  800. function cot_check_email($res)
  801. {
  802. return mb_strlen($res) > 4 && preg_match('#^[\w\p{L}][\.\w\p{L}\-]*@[\w\p{L}\.\-]+\.[\w\p{L}]+$#u', $res);
  803. }
  804. /**
  805. * Sends mail with standard PHP mail().
  806. * If cot_mail_custom() function exists, it will be called instead of the PHP
  807. * function. This way custom mail delivery methods, such as SMTP, are
  808. * supported.
  809. *
  810. * @global $cfg
  811. * @param string $fmail Recipient
  812. * @param string $subject Subject
  813. * @param string $body Message body
  814. * @param string $headers Message headers
  815. * @param bool $customtemplate Use custom template
  816. * @param string $additional_parameters Additional parameters passed to sendmail
  817. * @return bool
  818. */
  819. function cot_mail($fmail, $subject, $body, $headers='', $customtemplate = false, $additional_parameters = null, $html = false)
  820. {
  821. global $cfg, $cot_mail_senders;
  822. if (function_exists('cot_mail_custom'))
  823. {
  824. return cot_mail_custom($fmail, $subject, $body, $headers, $customtemplate, $additional_parameters, $html);
  825. }
  826. $ret = true;
  827. if (is_array($cot_mail_senders) && count($cot_mail_senders) > 0)
  828. {
  829. foreach ($cot_mail_senders as $func)
  830. {
  831. $ret &= $func($fmail, $subject, $body, $headers, $additional_parameters, $html);
  832. }
  833. return $ret;
  834. }
  835. if (empty($fmail)) return false;
  836. $sitemaintitle = mb_encode_mimeheader($cfg['maintitle'], 'UTF-8', 'B', "\n");
  837. $headers = (empty($headers)) ? "From: \"" . $sitemaintitle . "\" <" . $cfg['adminemail'] . ">\n" . "Reply-To: <" . $cfg['adminemail'] . ">\n"
  838. : $headers;
  839. $headers .= "Message-ID: <" . md5(uniqid(microtime())) . "@" . $_SERVER['SERVER_NAME'] . ">\n";
  840. $type_body = $html ? "html" : "plain";
  841. $headers .= "Content-Type: text/".$type_body."; charset=UTF-8\n";
  842. $headers .= "Content-Transfer-Encoding: 8bit\n";
  843. if (!$customtemplate)
  844. {
  845. $body_params = array(
  846. 'SITE_TITLE' => $cfg['maintitle'],
  847. 'SITE_URL' => $cfg['mainurl'],
  848. 'SITE_DESCRIPTION' => $cfg['subtitle'],
  849. 'ADMIN_EMAIL' => $cfg['adminemail'],
  850. 'MAIL_SUBJECT' => $subject,
  851. 'MAIL_BODY' => $body
  852. );
  853. $subject_params = array(
  854. 'SITE_TITLE' => $cfg['maintitle'],
  855. 'SITE_DESCRIPTION' => $cfg['subtitle'],
  856. 'MAIL_SUBJECT' => $subject
  857. );
  858. $subject = cot_title($cfg['subject_mail'], $subject_params, false);
  859. $body = cot_title(str_replace("\r\n", "\n", $cfg['body_mail']), $body_params, false);
  860. }
  861. $subject = mb_encode_mimeheader($subject, 'UTF-8', 'B', "\n");
  862. if (ini_get('safe_mode'))
  863. {
  864. mail($fmail, $subject, $body, $headers);
  865. }
  866. else
  867. {
  868. mail($fmail, $subject, $body, $headers, $additional_parameters);
  869. }
  870. return true;
  871. }
  872. /**
  873. * Allocate memory
  874. *
  875. * @param int $needMemory Memory to allocate in bytes
  876. * @return bool TRUE if enough memory is available, FALSE otherwise
  877. */
  878. function cot_memory_allocate($needMemory)
  879. {
  880. $needMemory = (int)$needMemory;
  881. if(empty($needMemory)) return false;
  882. // Getting memory occupied by the script (in bytes)
  883. $usedMem = memory_get_usage(true);
  884. $haveMem = ini_get('memory_limit');
  885. // no limit set, so we try any way
  886. if ($haveMem == '-1') return true;
  887. preg_match('/(\d+)(\w+)/', $haveMem, $mtch);
  888. if (!empty($mtch[2])) {
  889. $mtch[2] = mb_strtoupper($mtch[2]);
  890. if ($mtch[2] == 'G') {
  891. $haveMem = $mtch[1] * 1073741824;
  892. } elseif ($mtch[2] == 'M') {
  893. $haveMem = $mtch[1] * 1048576;
  894. } elseif ($mtch[2] == 'K') {
  895. $haveMem = $mtch[1] * 1024;
  896. }
  897. }
  898. $needMem = intval($needMemory + $usedMem);
  899. if ($haveMem < $needMem) {
  900. // Could not allocate memory
  901. if (!ini_set('memory_limit', $needMem)) return false;
  902. } else {
  903. return true;
  904. }
  905. // Making sure we could allocate enough memory
  906. $haveMem = ini_get('memory_limit');
  907. preg_match('/(\d+)(\w+)/', $haveMem, $mtch);
  908. if (!empty($mtch[2])) {
  909. $mtch[2] = mb_strtoupper($mtch[2]);
  910. if ($mtch[2] == 'G') {
  911. $haveMem = $mtch[1] * 1073741824;
  912. } elseif ($mtch[2] == 'M') {
  913. $haveMem = $mtch[1] * 1048576;
  914. } elseif ($mtch[2] == 'K') {
  915. $haveMem = $mtch[1] * 1024;
  916. }
  917. }
  918. // No, we couldn't allocate enough memory
  919. if ($haveMem < $needMem) return false;
  920. return true;
  921. }
  922. /**
  923. * Checks if a module is currently installed and active
  924. *
  925. * @global array $cot_modules Module registry
  926. * @param string $name Module name
  927. * @return bool
  928. */
  929. function cot_module_active($name)
  930. {
  931. global $cot_modules;
  932. return isset($cot_modules[$name]);
  933. }
  934. /**
  935. * Applies output filters, adds XSS protection to POST forms
  936. * Note: XSS can be switched off by adding "xp-off" class to form
  937. *
  938. * @param string $output
  939. * @return string
  940. */
  941. function cot_outputfilters($output)
  942. {
  943. /* === Hook === */
  944. foreach (cot_getextplugins('output') as $pl)
  945. {
  946. include realpath(dirname(__FILE__).'/..') . '/' . $pl;
  947. }
  948. /* ==== */
  949. $output = preg_replace_callback('#<form\s+[^>]*method=["\']?post["\']?[^>]*>#i', 'cot_outputfilters_callback', $output);
  950. return($output);
  951. }
  952. /**
  953. * Used with cot_outputfilters
  954. * It is needed because php 5.2 does not support anonymous functions. So during the installation we can not even show
  955. * an error message.
  956. * @param $m
  957. * @return string
  958. */
  959. function cot_outputfilters_callback($m)
  960. {
  961. return $m[0] . (preg_match('/class\s*=\s*["\']?.*?[\s"\']xp-off[\s"\'].*?["\']?/i', $m[0]) ? '' : cot_xp());
  962. }
  963. /**
  964. * Checks if a plugin is currently installed and active
  965. *
  966. * @global array $cot_plugins_active Active plugins registry
  967. * @param string $name Plugin name
  968. * @return bool
  969. */
  970. function cot_plugin_active($name)
  971. {
  972. global $cot_plugins_active;
  973. return is_array($cot_plugins_active) && isset($cot_plugins_active[$name]);
  974. }
  975. /**
  976. * Sends standard HTTP headers and disables browser cache
  977. *
  978. * @param string $content_type Content-Type value (without charset)
  979. * @param string $response_code HTTP response code, e.g. '404 Not Found'
  980. * @param int $last_modified Last modified time
  981. * @return bool
  982. */
  983. function cot_sendheaders($content_type = 'text/html', $response_code = '200 OK', $last_modified = 0)
  984. {
  985. global $sys;
  986. $protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
  987. $last_modified = (int)$last_modified > 0 ? (int)$last_modified : 0;
  988. if ($last_modified > 0)
  989. {
  990. $modified_since = (isset($_ENV['HTTP_IF_MODIFIED_SINCE'])) ? strtotime(substr($_ENV['HTTP_IF_MODIFIED_SINCE'], 5)) : false;
  991. $modified_since = (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) ? strtotime(substr($_SERVER['HTTP_IF_MODIFIED_SINCE'], 5)) : $modified_since;
  992. if ($modified_since && $modified_since >= $last_modified)
  993. {
  994. header($protocol . ' 304 Not Modified');
  995. exit;
  996. }
  997. }
  998. else
  999. {
  1000. $last_modified = $sys['now'] - 3600*12;
  1001. }
  1002. header($protocol . ' ' . $response_code);
  1003. header('Expires: Mon, Apr 01 1974 00:00:00 GMT');
  1004. header('Last-Modified: '.gmdate('D, d M Y H:i:s', $last_modified).' GMT');
  1005. header('Content-Type: '.$content_type.'; charset=UTF-8');
  1006. header('Cache-Control: no-store,no-cache,must-revalidate');
  1007. header('Cache-Control: post-check=0,pre-check=0', FALSE);
  1008. header('Pragma: no-cache');
  1009. return TRUE;
  1010. }
  1011. /**
  1012. * Set cookie with optional HttpOnly flag
  1013. * @param string $name The name of the cookie
  1014. * @param string $value The value of the cookie
  1015. * @param int $expire The time the cookie expires in unixtime
  1016. * @param string $path The path on the server in which the cookie will be available on.
  1017. * @param string $domain The domain that the cookie is available.
  1018. * @param bool $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection. When set to TRUE, the cookie will only be set if a secure connection exists.
  1019. * @param bool $httponly HttpOnly flag
  1020. * @return bool
  1021. */
  1022. function cot_setcookie($name, $value, $expire = '', $path='', $domain='', $secure = false, $httponly = true)
  1023. {
  1024. global $cfg;
  1025. if (mb_strpos($domain, '.') === FALSE)
  1026. {
  1027. // Some browsers don't support cookies for local domains
  1028. $domain = '';
  1029. }
  1030. $domain = (empty($domain)) ? $cfg['cookiedomain'] : $domain;
  1031. $path = (empty($path)) ? $cfg['cookiepath'] : $path;
  1032. $expire = (empty($expire)) ? time()+$cfg['cookielifetime'] : $expire;
  1033. if ($domain != '' && $domain != 'localhost')
  1034. {
  1035. // Make sure www. is stripped and leading dot is added for subdomain support on some browsers
  1036. if (mb_strtolower(mb_substr($domain, 0, 4)) == 'www.')
  1037. {
  1038. $domain = mb_substr($domain, 4);
  1039. }
  1040. if ($domain[0] != '.')
  1041. {
  1042. $domain = '.'.$domain;
  1043. }
  1044. }
  1045. else
  1046. {
  1047. $domain = false;
  1048. }
  1049. return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
  1050. }
  1051. /**
  1052. * Performs actions required right before shutdown
  1053. * @global CotDB $db
  1054. * @global Cache $cache
  1055. */
  1056. function cot_shutdown()
  1057. {
  1058. global $cache, $db;
  1059. // Clear import buffer if everything's OK on POST
  1060. if ($_SERVER['REQUEST_METHOD'] == 'POST' && !cot_error_found())
  1061. {
  1062. unset($_SESSION['cot_buffer']);
  1063. }
  1064. while (ob_get_level() > 0)
  1065. {
  1066. ob_end_flush();
  1067. }
  1068. // Need to destroy cache before DB connection is lost
  1069. $cache && $cache->db && $cache->db->flush();
  1070. $cache = null;
  1071. $db = null;
  1072. }
  1073. /**
  1074. * Generates a title string by replacing submasks with assigned values
  1075. *
  1076. * @param string $area Area maskname or actual mask
  1077. * @param array $params An associative array of available parameters
  1078. * @param bool $escape Escape HTML special characters
  1079. * @return string
  1080. */
  1081. function cot_title($mask, $params = array(), $escape = true)
  1082. {
  1083. global $cfg;
  1084. $res = (!empty(cot::$cfg[$mask])) ? cot::$cfg[$mask] : $mask;
  1085. is_array($params) ? $args = $params : mb_parse_str($params, $args);
  1086. if (preg_match_all('#\{(.+?)\}#', $res, $matches, PREG_SET_ORDER)) {
  1087. foreach($matches as $m) {
  1088. $var = $m[1];
  1089. if(isset($args[$var])) {
  1090. $val = $escape ? htmlspecialchars($args[$var], ENT_COMPAT, 'UTF-8', false) : $args[$var];
  1091. } else {
  1092. $val = '';
  1093. }
  1094. $res = str_replace($m[0], $val, $res);
  1095. }
  1096. }
  1097. return $res;
  1098. }
  1099. /**
  1100. * Generates random string within hexadecimal range
  1101. *
  1102. * @param int $length Length
  1103. * @return string
  1104. */
  1105. function cot_unique($length = 16)
  1106. {
  1107. $string = sha1(mt_rand());
  1108. if ($length > 40)
  1109. {
  1110. for ($i=0; $i < floor($length / 40); $i++)
  1111. {
  1112. $string .= sha1(mt_rand());
  1113. }
  1114. }
  1115. return(substr($string, 0, $length));
  1116. }
  1117. /**
  1118. * Generates random string within specified charlist
  1119. *
  1120. * @param int $length String length
  1121. * @param string $charlist Allowed characters, defaults to alphanumeric chars
  1122. * @return string and numbers ($pass)
  1123. */
  1124. function cot_randomstring($length = 8, $charlist = null)
  1125. {
  1126. if (!is_string($charlist)) $charlist = 'ABCDEFGHIJKLMNOPRSTUVYZabcdefghijklmnoprstuvyz0123456789';
  1127. $max = strlen($charlist) - 1;
  1128. $string = '';
  1129. for ($i=0; $i < $length; $i++)
  1130. {
  1131. $string .= $charlist[mt_rand(0, $max)];
  1132. }
  1133. return $string;
  1134. }
  1135. /*
  1136. * =========================== Structure functions ===========================
  1137. */
  1138. /**
  1139. * Loads comlete category structure into array
  1140. * @global CotDB $db
  1141. */
  1142. function cot_load_structure()
  1143. {
  1144. global $db, $db_structure, $cfg, $cot_extrafields, $structure;
  1145. if (function_exists('cot_load_structure_custom'))
  1146. {
  1147. return cot_load_structure_custom();
  1148. }
  1149. $structure = array();
  1150. if (defined('COT_UPGRADE'))
  1151. {
  1152. $sql = $db->query("SELECT * FROM $db_structure ORDER BY structure_path ASC");
  1153. $row['structure_area'] = 'page';
  1154. }
  1155. else
  1156. {
  1157. $sql = $db->query("SELECT * FROM $db_structure ORDER BY structure_area ASC, structure_path ASC");
  1158. }
  1159. /* == Hook: Part 1 ==*/
  1160. $extp = cot_getextplugins('structure');
  1161. /* ================= */
  1162. $path = array(); // code path tree
  1163. $tpath = array(); // title path tree
  1164. $tpls = array(); // tpl codes tree
  1165. foreach ($sql->fetchAll() as $row)
  1166. {
  1167. $last_dot = mb_strrpos($row['structure_path'], '.');
  1168. $row['structure_tpl'] = empty($row['structure_tpl']) ? $row['structure_code'] : $row['structure_tpl'];
  1169. if ($last_dot > 0)
  1170. {
  1171. $path1 = mb_substr($row['structure_path'], 0, $last_dot);
  1172. $path[$row['structure_path']] = $path[$path1] . '.' . $row['structure_code'];
  1173. $separaror = ($cfg['separator'] == strip_tags($cfg['separator'])) ? ' ' . $cfg['separator'] . ' ' : ' \ ';
  1174. $tpath[$row['structure_path']] = $tpath[$path1] . $separaror . $row['structure_title'];
  1175. $parent_dot = mb_strrpos($path[$path1], '.');
  1176. $parent = ($parent_dot > 0) ? mb_substr($path[$path1], $parent_dot + 1) : $path[$path1];
  1177. }
  1178. else
  1179. {
  1180. $path[$row['structure_path']] = $row['structure_code'];
  1181. $tpath[$row['structure_path']] = $row['structure_title'];
  1182. $parent = $row['structure_code']; // self
  1183. }
  1184. if ($row['structure_tpl'] == 'same_as_parent')
  1185. {
  1186. $row['structure_tpl'] = $tpls[$parent];
  1187. }
  1188. $tpls[$row['structure_code']] = $row['structure_tpl'];
  1189. $structure[$row['structure_area']][$row['structure_code']] = array(
  1190. 'path' => $path[$row['structure_path']],
  1191. 'tpath' => $tpath[$row['structure_path']],
  1192. 'rpath' => $row['structure_path'],
  1193. 'id' => $row['structure_id'],
  1194. 'tpl' => $row['structure_tpl'],
  1195. 'title' => $row['structure_title'],
  1196. 'desc' => $row['structure_desc'],
  1197. 'icon' => $row['structure_icon'],
  1198. 'locked' => $row['structure_locked'],
  1199. 'count' => $row['structure_count']
  1200. );
  1201. if (is_array($cot_extrafields[$db_structure]))
  1202. {
  1203. foreach ($cot_extrafields[$db_structure] as $exfld)
  1204. {
  1205. $structure[$row['structure_area']][$row['structure_code']][$exfld['field_name']] = $row['structure_'.$exfld['field_name']];
  1206. }
  1207. }
  1208. /* == Hook: Part 2 ==*/
  1209. foreach ($extp as $pl)
  1210. {
  1211. include $pl;
  1212. }
  1213. /* ================= */
  1214. }
  1215. }
  1216. /**
  1217. * Gets an array of category children
  1218. *
  1219. * @param string $area Area code
  1220. * @param string $cat Cat code
  1221. * @param bool $allsublev All sublevels array
  1222. * @param bool $firstcat Add main cat
  1223. * @param bool $userrights Check userrights
  1224. * @param bool $sqlprep use $db->prep function
  1225. * @return array
  1226. * @global CotDB $db
  1227. */
  1228. function cot_structure_children($area, $cat, $allsublev = true, $firstcat = true, $userrights = true, $sqlprep = true)
  1229. {
  1230. global $structure, $db;
  1231. $mtch = '';
  1232. $mtchlen = $mtchlvl = 0;
  1233. if ($cat != '')
  1234. {
  1235. $mtch = $structure[$area][$cat]['path'] . '.';
  1236. $mtchlen = mb_strlen($mtch);
  1237. $mtchlvl = mb_substr_count($mtch, ".");
  1238. }
  1239. $catsub = array();
  1240. if ($cat != '' && $firstcat && (($userrights && cot_auth($area, $cat, 'R') || !$userrights)))
  1241. {
  1242. $catsub[] = $cat;
  1243. }
  1244. foreach ($structure[$area] as $i => $x)
  1245. {
  1246. if (($cat == '' || mb_substr($x['path'], 0, $mtchlen) == $mtch) && (($userrights && cot_auth($area, $i, 'R') || !$userrights)))
  1247. {
  1248. //$subcat = mb_substr($x['path'], $mtchlen + 1);
  1249. if ($allsublev || (!$allsublev && mb_substr_count($x['path'],".") == $mtchlvl))
  1250. {
  1251. $i = ($sqlprep) ? $db->prep($i) : $i;
  1252. $catsub[] = $i;
  1253. }
  1254. }
  1255. }
  1256. return($catsub);
  1257. }
  1258. /**
  1259. * Gets an array of category parents
  1260. *
  1261. * @param string $area Area code
  1262. * @param string $cat Cat code
  1263. * @param string $type Type 'full', 'first', 'last'
  1264. * @return mixed
  1265. */
  1266. function cot_structure_parents($area, $cat, $type = 'full')
  1267. {
  1268. global $structure;
  1269. $pathcodes = explode('.', $structure[$area][$cat]['path']);
  1270. if ($type == 'first')
  1271. {
  1272. return $pathcodes[0];
  1273. }
  1274. elseif ($type == 'last')
  1275. {
  1276. return (count($pathcodes) > 1) ? $pathcodes[count($pathcodes) - 2] : null;
  1277. }
  1278. return $pathcodes;
  1279. }
  1280. /*
  1281. * ================================= Authorization Subsystem ==================================
  1282. */
  1283. /**
  1284. * Returns specific access permissions
  1285. *
  1286. * @param string $area Cotonti area
  1287. * @param string $option Option to access
  1288. * @param string $mask Access mask
  1289. * @return mixed
  1290. */
  1291. function cot_auth($area, $option, $mask = 'RWA')
  1292. {
  1293. global $sys, $usr;
  1294. $mn['R'] = 1;
  1295. $mn['W'] = 2;
  1296. $mn['1'] = 4;
  1297. $mn['2'] = 8;
  1298. $mn['3'] = 16;
  1299. $mn['4'] = 32;
  1300. $mn['5'] = 64;
  1301. $mn['A'] = 128;
  1302. $masks = str_split($mask);
  1303. $res = array();
  1304. foreach ($masks as $k => $ml) {
  1305. if (empty($mn[$ml])) {
  1306. cot::$sys['auth_log'][] = $area.'.'.$option.'.'.$ml.'=0';
  1307. $res[] = FALSE;
  1308. } elseif ($option == 'any') {
  1309. $cnt = 0;
  1310. if(is_array(cot::$usr['auth'])) {
  1311. if (isset(cot::$usr['auth'][$area]) && is_array(cot::$usr['auth'][$area])) {
  1312. foreach (cot::$usr['auth'][$area] as $k => $g) {
  1313. $cnt += (($g & $mn[$ml]) == $mn[$ml]);
  1314. }
  1315. }
  1316. $cnt = ($cnt == 0 && cot::$usr['auth']['admin']['a'] && $ml == 'A') ? 1 : $cnt;
  1317. }
  1318. cot::$sys['auth_log'][] = ($cnt > 0) ? $area.'.'.$option.'.'.$ml.'=1' : $area.'.'.$option.'.'.$ml.'=0';
  1319. $res[] = ($cnt > 0) ? TRUE : FALSE;
  1320. } else {
  1321. $tmpOption = 0;
  1322. if (isset(cot::$usr['auth'][$area][$option])) $tmpOption = cot::$usr['auth'][$area][$option];
  1323. cot::$sys['auth_log'][] = (($tmpOption & $mn[$ml]) == $mn[$ml]) ? $area.'.'.$option.'.'.$ml.'=1' : $area.'.'.$option.'.'.$ml.'=0';
  1324. $res[] = (($tmpOption & $mn[$ml]) == $mn[$ml]) ? TRUE : FALSE;
  1325. }
  1326. }
  1327. return (count($res) == 1) ? $res[0] : $res;
  1328. }
  1329. /**
  1330. * Builds Access Control List (ACL) for a specific user
  1331. *
  1332. * @param int $userid User ID
  1333. * @param int $maingrp User main group
  1334. * @return array
  1335. * @global CotDB $db
  1336. */
  1337. function cot_auth_build($userid, $maingrp = 0)
  1338. {
  1339. global $db, $db_auth, $db_groups_users;
  1340. $groups = array();
  1341. $authgrid = array();
  1342. if ($userid == 0 || $maingrp == 0) {
  1343. $groups[] = 1;
  1344. } else {
  1345. $groups[] = $maingrp;
  1346. $sql = $db->query("SELECT gru_groupid FROM $db_groups_users WHERE gru_userid=$userid");
  1347. while ($row = $sql->fetch()) {
  1348. $groups[] = $row['gru_groupid'];
  1349. }
  1350. $sql->closeCursor();
  1351. }
  1352. $sql_groups = implode(',', $groups);
  1353. $sql = $db->query("SELECT auth_code, auth_option, auth_rights FROM $db_auth WHERE auth_groupid IN (".$sql_groups.") ORDER BY auth_code ASC, auth_option ASC");
  1354. while ($row = $sql->fetch()) {
  1355. $authgrid[$row['auth_code']][$row['auth_option']] = 0;
  1356. $authgrid[$row['auth_code']][$row['auth_option']] |= $row['auth_rights'];
  1357. }
  1358. $sql->closeCursor();
  1359. return $authgrid;
  1360. }
  1361. /**
  1362. * Block user if he is not allowed to access the page
  1363. *
  1364. * @param bool $allowed Authorization result
  1365. * @return bool
  1366. */
  1367. function cot_block($allowed)
  1368. {
  1369. if (!$allowed)
  1370. {
  1371. global $sys, $env;
  1372. $env['status'] = '403 Forbidden';
  1373. cot_redirect(cot_url('message', 'msg=930&'.$sys['url_redirect'], '', true));
  1374. }
  1375. return FALSE;
  1376. }
  1377. /**
  1378. * Block guests from viewing the page
  1379. *
  1380. * @return bool
  1381. */
  1382. function cot_blockguests()
  1383. {
  1384. global $env, $usr, $sys;
  1385. if ($usr['id'] < 1)
  1386. {
  1387. $env['status'] = '403 Forbidden';
  1388. cot_redirect(cot_url('message', "msg=930&".$sys['url_redirect'], '', true));
  1389. }
  1390. return FALSE;
  1391. }
  1392. /**
  1393. * Authorize user
  1394. *
  1395. * @param int $id User ID
  1396. * @param bool|null $remember remember user authorization
  1397. * @return bool
  1398. *
  1399. * @todo May be we should optionally fill user data like in system/common.php on line 336
  1400. * It can be useful if we will not redirect user after login, may be we should redirect anyway
  1401. */
  1402. function cot_user_authorize($id, $remember = null)
  1403. {
  1404. if(is_null($remember) && cot::$cfg['forcerememberme'])
  1405. {
  1406. $remember = true;
  1407. }
  1408. if(is_array($id) && isset($id['user_id']) && isset($id['user_password']))
  1409. {
  1410. $user = $id;
  1411. $id = $user['user_id'];
  1412. }
  1413. else
  1414. {
  1415. $id = (int)$id;
  1416. if($id <= 0) return false;
  1417. $res = cot::$db->query("SELECT user_id, user_password, user_maingrp, user_banexpire, user_sid, ".
  1418. "user_sidtime, user_passsalt, user_passfunc FROM ".cot::$db->users." WHERE user_id = ? LIMIT 1", $id);
  1419. $user = $res->fetch();
  1420. if($user <= 0) return false;
  1421. }
  1422. if ($user['user_maingrp'] == COT_GROUP_BANNED)
  1423. {
  1424. if (cot::$sys['now'] > $user['user_banexpire'] && $user['user_banexpire'] > 0)
  1425. {
  1426. cot::$db->update(cot::$db->users, array('user_maingrp' => COT_GROUP_MEMBERS), "user_id={$user['user_id']}");
  1427. $row['user_maingrp'] = COT_GROUP_MEMBERS;
  1428. }
  1429. else
  1430. {
  1431. return false;
  1432. }
  1433. }
  1434. $token = cot_unique(16);
  1435. $sid = hash_hmac('sha256', $user['user_password'] . $user['user_sidtime'], cot::$cfg['secret_key']);
  1436. $update_sid = '';
  1437. if (empty($user['user_sid']) || $user['user_sid'] != $sid
  1438. || $user['user_sidtime'] + cot::$cfg['cookielifetime'] < cot::$sys['now'])
  1439. {
  1440. // Generate new session identifier
  1441. $sid = hash_hmac('sha256', $user['user_password'] . cot::$sys['now'], cot::$cfg['secret_key']);
  1442. $update_sid = ", user_sid = " . cot::$db->quote($sid) . ", user_sidtime = " . cot::$sys['now'];
  1443. }
  1444. cot::$db->query("UPDATE ".cot::$db->users." SET user_lastip='".cot::$usr['ip']."', user_lastlog = ".cot::$sys['now'].
  1445. ", user_logcount = user_logcount + 1, user_token = ".cot::$db->quote($token).
  1446. " $update_sid WHERE user_id={$user['user_id']}");
  1447. // Hash the sid once more so it can't be faked even if you know user_sid
  1448. $sid = hash_hmac('sha1', $sid, cot::$cfg['secret_key']);
  1449. $u = base64_encode($user['user_id'].':'.$sid);
  1450. /* === Hook === */
  1451. foreach (cot_getextplugins('users.authorize') as $pl)
  1452. {
  1453. include $pl;
  1454. }
  1455. /* ===== */
  1456. if($remember)
  1457. {
  1458. cot_setcookie(cot::$sys['site_id'], $u, time()+ cot::$cfg['cookielifetime'], cot::$cfg['cookiepath'],
  1459. cot::$cfg['cookiedomain'], cot::$sys['secure'], true);
  1460. unset($_SESSION[cot::$sys['site_id']]);
  1461. }
  1462. else
  1463. {
  1464. $_SESSION[cot::$sys['site_id']] = $u;
  1465. }
  1466. /* === Hook === */
  1467. foreach (cot_getextplugins('users.authorize.done') as $pl)
  1468. {
  1469. include $pl;
  1470. }
  1471. /* ===== */
  1472. return true;
  1473. }
  1474. /*
  1475. * =========================== Output forming functions ===========================
  1476. */
  1477. /**
  1478. * Renders breadcrumbs string from array of path crumbs
  1479. *
  1480. * @param array $crumbs Path crumbs as an array: { {$url1, $title1}, {$url2, $title2},..}
  1481. * @param bool $home Whether to include link to home page in the root
  1482. * @param bool $nolast If TRUE, last crumb will be rendered as plain text rather than hyperlink
  1483. * @param bool $plain If TRUE plain titles will be rendered instead of hyperlinks
  1484. * @param string $inrc Item template
  1485. * @param string $separator Items separator
  1486. * @return string
  1487. */
  1488. function cot_breadcrumbs($crumbs, $home = true, $nolast = true, $plain = false, $inrc = '', $separator = '')
  1489. {
  1490. global $cfg, $L;
  1491. $tmp = array();
  1492. if ($home)
  1493. {
  1494. $maintitle = (empty($L['breadcrumbmaintitle'])) ? $cfg['maintitle'] : $L['breadcrumbmaintitle'];
  1495. array_unshift($crumbs, array(cot_url('index'), $maintitle));
  1496. }
  1497. $cnt = count($crumbs);
  1498. for ($i = 0; $i < $cnt; $i++)
  1499. {
  1500. $elem = '';
  1501. $params = is_array($crumbs[$i]) ? array(
  1502. 'url' => (!empty($crumbs[$i][0])) ? $crumbs[$i][0] : '#',
  1503. 'title' => htmlspecialchars($crumbs[$i][1], ENT_COMPAT, 'UTF-8', false)
  1504. ) : $params = array('title' => $crumbs[$i]);
  1505. if ($plain || ($nolast && $i === $cnt - 1) || !isset($params['url']))
  1506. {
  1507. $crumb = cot_rc('breadcrumbs_plain', $params);
  1508. if ($crumb == 'breadcrumbs_plain')
  1509. {
  1510. $crumb = cot_rc('string_catpath', $params);
  1511. }
  1512. }
  1513. else
  1514. {
  1515. $crumb = cot_rc('breadcrumbs_link', $params);
  1516. if ($crumb == 'breadcrumbs_link')
  1517. {
  1518. $crumb = cot_rc('link_catpath', $params);
  1519. }
  1520. }
  1521. if ($i == 0)
  1522. {
  1523. $elem = cot_rc('breadcrumbs_first', array('crumb' => $crumb));
  1524. }
  1525. if ($i == $cnt - 1)
  1526. {
  1527. $elem = cot_rc('breadcrumbs_last', array('crumb' => $crumb));
  1528. }
  1529. if (!$elem || $elem == 'breadcrumbs_first' || $elem == 'breadcrumbs_last')
  1530. {
  1531. $elem = cot_rc('breadcrumbs_crumb', array('crumb' => $crumb));
  1532. }
  1533. if ($elem == 'breadcrumbs_crumb')
  1534. {
  1535. $elem = $crumb;
  1536. }
  1537. if(!empty($inrc))
  1538. {
  1539. $elem = cot_rc($inrc, array('elem' => $elem));
  1540. }
  1541. $tmp[] = $elem;
  1542. }
  1543. $separator = (!empty($separator) || !empty($inrc)) ? $separator : cot_rc('breadcrumbs_separator');
  1544. $separator = ($separator == 'breadcrumbs_separator') ? $cfg['separator'] : $separator;
  1545. $separator = (!empty($inrc) && (mb_strlen($separator) > 2 || empty($separator))) ? $separator : ' '.$separator.' ';
  1546. $breadcrumbs = implode($separator, $tmp);
  1547. $container = cot_rc('breadcrumbs_container', array('crumbs' => $breadcrumbs));
  1548. return ($container == 'breadcrumbs_container') ? $breadcrumbs : $container;
  1549. }
  1550. /**
  1551. * Calculates age out of date of birth.
  1552. *
  1553. * @param int $birthdate Timestamp or a string according to format 'YYYY-MM-DD'
  1554. * @return int Age in years or NULL on failure
  1555. */
  1556. function cot_build_age($birthdate)
  1557. {
  1558. if (is_string($birthdate))
  1559. {
  1560. $birthdate = strtotime($birthdate);
  1561. }
  1562. if (is_null($birthdate) || $birthdate === false || $birthdate === -1)
  1563. {
  1564. return null;
  1565. }
  1566. list($birth_y, $birth_m, $birth_d) = explode('-', cot_date('Y-m-d', $birthdate));
  1567. list($now_y, $now_m, $now_d) = explode('-', cot_date('Y-m-d'));
  1568. $age = $now_y - $birth_y - 1;
  1569. if ($birth_m < $now_m || ($birth_m == $now_m && $birth_d <= $now_d))
  1570. {
  1571. $age += 1;
  1572. }
  1573. return ($age < 0) ? $age + 136 : $age;
  1574. }
  1575. /**
  1576. * Builds category path for cot_breadcrumbs()
  1577. *
  1578. * @param string $area Area code
  1579. * @param string $cat Category code
  1580. * @return array
  1581. * @see cot_breadcrumbs()
  1582. */
  1583. function cot_structure_buildpath($area, $cat)
  1584. {
  1585. global $structure;
  1586. $tmp = array();
  1587. if (isset(cot::$structure[$area][$cat]['path'])) {
  1588. $pathcodes = explode('.', cot::$structure[$area][$cat]['path']);
  1589. foreach ($pathcodes as $x) {
  1590. if ($x != 'system') {
  1591. $tmp[] = array(cot_url($area, 'c=' . $x), cot::$structure[$area][$x]['title']);
  1592. }
  1593. }
  1594. }
  1595. return $tmp;
  1596. }
  1597. /**
  1598. * Returns country text button
  1599. *
  1600. * @param string $flag Country code
  1601. * @return string
  1602. */
  1603. function cot_build_country($flag)
  1604. {
  1605. global $cot_countries;
  1606. if (!$cot_countries) include_once cot_langfile('countries', 'core');
  1607. $flag = (empty($flag)) ? '00' : $flag;
  1608. $country = isset($cot_countries[$flag]) ? $cot_countries[$flag] : cot::$R['code_option_empty'];
  1609. return cot_rc_link(cot_url('users', 'f=country_'.$flag), $country, array(
  1610. 'title' => $country
  1611. ));
  1612. }
  1613. /**
  1614. * Returns user email link
  1615. *
  1616. * @param string $email E-mail address
  1617. * @param bool $hide Hide email option
  1618. * @return string
  1619. */
  1620. function cot_build_email($email, $hide = false)
  1621. {
  1622. global $L;
  1623. if ($hide)
  1624. {
  1625. return $L['Hidden'];
  1626. }
  1627. elseif (!empty($email) && cot_check_email($email))
  1628. {
  1629. $link = cot_rc('link_email', array('email' => $email));
  1630. return function_exists('cot_obfuscate') ? cot_obfuscate($link) : $link;
  1631. }
  1632. }
  1633. /**
  1634. * Generate human-readable filesize.
  1635. *
  1636. * @param float $bytes
  1637. * Filesize in bytes
  1638. * @param int $decimals
  1639. * Number of decimals to show.
  1640. * @param mixed $round
  1641. * Round up to this number of decimals.
  1642. * Set false to disable or null to inherit from $decimals.
  1643. * @param bool $binary Use binary instead of decimal calculation.
  1644. * Set TRUE for the IEC binary standard where 1 Kibibyte = 1024 bytes
  1645. * Set FALSE for the SI/IEEE decimal standard where 1 Kilobyte = 1000 bytes
  1646. * @param string $smallestunit
  1647. * Key of the smallest unit to show. Any number smaller than this will return 'Less than 1 ...'.
  1648. * Effectively its a way to cut off $units at a certain key.
  1649. * @return string
  1650. */
  1651. function cot_build_filesize($bytes, $decimals = 0, $round = null, $binary = false, $smallestunit = null)
  1652. {
  1653. global $Ls;
  1654. $sc_sign = ' '; // leading space for keys index cast as string
  1655. $units = $binary ? array(
  1656. $sc_sign . 1099511627776 => $Ls['Tebibytes'],
  1657. $sc_sign . 1073741824 => $Ls['Gibibytes'],
  1658. $sc_sign . 1048576 => $Ls['Mebibytes'],
  1659. $sc_sign . 1024 => $Ls['Kibibytes'],
  1660. $sc_sign . 1 => $Ls['Bytes'],
  1661. ) : array(
  1662. $sc_sign . 1000000000000 => $Ls['Terabytes'],
  1663. $sc_sign . 1000000000 => $Ls['Gigabytes'],
  1664. $sc_sign . 1000000 => $Ls['Megabytes'],
  1665. $sc_sign . 1000 => $Ls['Kilobytes'],
  1666. $sc_sign . 1 => $Ls['Bytes']
  1667. );
  1668. if ($smallestunit) $smallestunit = $sc_sign . $smallestunit;
  1669. $sizes = array_keys($units);
  1670. if ($bytes < $sizes[sizeof($units)-2]) $decimals = 0; // as byte can not be fractional
  1671. return cot_build_friendlynumber($bytes, $units, 1, $decimals, $round, $smallestunit);
  1672. }
  1673. /**
  1674. * Returns country flag button
  1675. *
  1676. * @param string $flag Country code
  1677. * @return string
  1678. */
  1679. function cot_build_flag($flag)
  1680. {
  1681. global $cot_countries;
  1682. if (!$cot_countries) include_once cot_langfile('countries', 'core');
  1683. $flag = (empty($flag)) ? '00' : $flag;
  1684. $country = isset($cot_countries[$flag]) ? $cot_countries[$flag] : cot::$R['code_option_empty'];
  1685. return cot_rc_link(cot_url('users', 'f=country_'.$flag),
  1686. cot_rc('icon_flag', array('code' => $flag, 'alt' => $flag)),
  1687. array('title' => $country)
  1688. );
  1689. }
  1690. /**
  1691. * Generic function for generating a human-readable number with localized units.
  1692. *
  1693. * @param float $number
  1694. * Input number to convert, based on the unit with size (key) 1.
  1695. * @param array $units
  1696. * Array of units as $relativesize => $unit.
  1697. * Example: array('3600' => 'hours', '60' => 'minutes', '1' => 'seconds').
  1698. * Where 'seconds' is the base unit, since it has a value of 1. Hours has a value of
  1699. * 3600, since one hour contains 3600 seconds. Values can be given as strings or integers.
  1700. * @param int $levels
  1701. * Number of levels to return.
  1702. * "3 hours 45 minutes" = 2 levels.
  1703. * @param int $decimals
  1704. * Number of decimals to show in last level.
  1705. * "2 minutes 20.5 seconds" = 2 levels, 1 decimals.
  1706. * @param mixed $round
  1707. * Number of decimals to round the last level up to, can also be negative, see round().
  1708. * Set false to disable or null to inherit from $decimals.
  1709. * @param string $smallestunit
  1710. * Key of the smallest unit to show. Any number smaller than this will return 'Less than 1 ...'.
  1711. * Effectively its a way to cut off $units at a certain key.
  1712. * @return string
  1713. */
  1714. function cot_build_friendlynumber($number, $units, $levels = 1, $decimals = 0, $round = null, $smallestunit = null)
  1715. {
  1716. global $L;
  1717. if (!is_array($units)) return '';
  1718. $pieces = array();
  1719. // First sort from big to small
  1720. ksort($units, SORT_NUMERIC);
  1721. $units = array_reverse($units, true);
  1722. // Trim units after $smallestunit
  1723. if (array_key_exists($smallestunit, $units))
  1724. {
  1725. $offset = array_search($smallestunit, array_keys($units));
  1726. $units = array_slice($units, 0, $offset+1, true);
  1727. }
  1728. if ($number == 0)
  1729. {
  1730. // Return smallest possible unit
  1731. $units = array_reverse(array_values($units));
  1732. return cot_declension(0, $units[0]);
  1733. }
  1734. foreach ($units as $size => $expr)
  1735. {
  1736. $size = floatval($size);
  1737. if ($number >= $size)
  1738. {
  1739. $levels--;
  1740. $num = $number / $size;
  1741. $number -= floor($num) * $size;
  1742. if ($number > 0 && $levels > 0)
  1743. {
  1744. // There's more to come, so no decimals yet.
  1745. $pieces[] = cot_declension(floor($num), $expr);
  1746. }
  1747. else
  1748. {
  1749. // Last item gets decimals and rounding.
  1750. if($decimals > 0)
  1751. {
  1752. $pieces[] = cot_build_number($num, $decimals, $round). ' ' .
  1753. cot_declension($num, $expr, true, true);
  1754. }
  1755. else
  1756. {
  1757. $pieces[] = floor($num). ' ' .
  1758. cot_declension(floor($num), $expr, true, true);
  1759. }
  1760. break;
  1761. }
  1762. if ($levels == 0)
  1763. {
  1764. break;
  1765. }
  1766. }
  1767. }
  1768. if (count($pieces) == 0)
  1769. {
  1770. // Smaller than smallest possible unit
  1771. $expr = array_reverse(array_values($units));
  1772. return $L['LessThan'] . ' ' . cot_declension(1, $expr[0]);
  1773. }
  1774. return implode(' ', $pieces);
  1775. }
  1776. /**
  1777. * Returns IP Search link
  1778. *
  1779. * @param string $ip IP mask
  1780. * @return string
  1781. */
  1782. function cot_build_ipsearch($ip)
  1783. {
  1784. global $sys;
  1785. if (!empty($ip))
  1786. {
  1787. if(cot_plugin_active('ipsearch'))
  1788. {
  1789. return cot_rc_link(cot_url('admin', 'm=other&p=ipsearch&a=search&id='.$ip.'&x='.$sys['xk']), $ip);
  1790. }
  1791. else
  1792. {
  1793. return $ip;
  1794. }
  1795. }
  1796. return '';
  1797. }
  1798. /**
  1799. * Wrapper for number_format() using locale number formatting and optional rounding.
  1800. *
  1801. * @param float $number
  1802. * Number to format
  1803. * @param int $decimals
  1804. * Number of decimals to return
  1805. * @param mixed $round
  1806. * Round up to this number of decimals.
  1807. * Set false to disable or null to inherit from $decimals.
  1808. * @return string
  1809. */
  1810. function cot_build_number($number, $decimals = 0, $round = null)
  1811. {
  1812. global $Ln;
  1813. if ($round === null) $round = $decimals;
  1814. if ($round !== false)
  1815. {
  1816. $number = round($number, $round);
  1817. }
  1818. return number_format($number, $decimals, $Ln['decimal_point'], $Ln['thousands_separator']);
  1819. }
  1820. /**
  1821. * Odd/even class choser for row
  1822. *
  1823. * @param int $number Row number
  1824. * @return string
  1825. */
  1826. function cot_build_oddeven($number)
  1827. {
  1828. $number = (int)$number;
  1829. return ($number % 2 == 0 ) ? 'even' : 'odd';
  1830. }
  1831. /**
  1832. * Returns stars image for user level
  1833. *
  1834. * @param int $level User level
  1835. * @return string
  1836. */
  1837. function cot_build_stars($level)
  1838. {
  1839. if ($level > 0 and $level < 100)
  1840. {
  1841. $stars = floor($level / 10) + 1;
  1842. return cot_rc('icon_stars', array('val' => $stars));
  1843. }
  1844. else
  1845. {
  1846. return '';
  1847. }
  1848. }
  1849. /**
  1850. * Returns readable time difference or 'Just now'.
  1851. *
  1852. * @param int $time Timestamp
  1853. * @param int $recently Seconds during which to show 'Just now'
  1854. * @return string
  1855. */
  1856. function cot_build_timeago($time, $recently = 60)
  1857. {
  1858. global $L, $sys;
  1859. if ($sys['now'] - $time < $recently)
  1860. {
  1861. return $L['JustNow'];
  1862. }
  1863. return cot_build_timegap($time) . ' ' . $L['Ago'];
  1864. }
  1865. /**
  1866. * Returns time gap between two timestamps
  1867. *
  1868. * @param int $t1
  1869. * Timestamp 1 (oldest, smallest value).
  1870. * @param int $t2
  1871. * Timestamp 2 (latest, largest value).
  1872. * @param int $levels
  1873. * Number of concatenated units to return.
  1874. * @param int $decimals
  1875. * Number of decimals to show on last level.
  1876. * @param mixed $round
  1877. * Round up last level to this number of decimals.
  1878. * Set false to disable or null to inherit from $decimals.
  1879. * @param string $smallestunit
  1880. * Key of the smallest unit to show. Any number smaller than this will return 'Less than 1 ...'.
  1881. * Effectively its a way to cut off $units at a certain key.
  1882. * @return string
  1883. */
  1884. function cot_build_timegap($t1, $t2 = null, $levels = 1, $decimals = 0, $round = null, $smallestunit = null)
  1885. {
  1886. global $Ls, $sys;
  1887. $units = array(
  1888. '31536000' => $Ls['Years'],
  1889. '2592000' => $Ls['Months'],
  1890. '604800' => $Ls['Weeks'],
  1891. '86400' => $Ls['Days'],
  1892. '3600' => $Ls['Hours'],
  1893. '60' => $Ls['Minutes'],
  1894. '1' => $Ls['Seconds'],
  1895. '0.001' => $Ls['Milliseconds']
  1896. );
  1897. if ($t2 === null)
  1898. {
  1899. $t2 = $sys['now'];
  1900. }
  1901. $gap = $t2 - $t1;
  1902. return cot_build_friendlynumber($gap, $units, $levels, $decimals, $round, $smallestunit);
  1903. }
  1904. /**
  1905. * Returns timezone offset formatted according to ISO 8601
  1906. *
  1907. * @param float $offset Timezone offset in seconds or hours. Set NULL for unknown timezone.
  1908. * @param bool $withgmt Include 'UTC' in the returned string.
  1909. * @param bool $short Use format without minutes, like GMT+1
  1910. * @return string Textual timezone like GMT+1:00
  1911. */
  1912. function cot_build_timezone($offset, $withgmt = true, $short = false)
  1913. {
  1914. $gmt = $withgmt ? 'UTC' : '';
  1915. if (is_null($offset)) {
  1916. return $short ? "$gmt-00" : "$gmt-00:00";
  1917. }
  1918. if ($offset == 0) {
  1919. return $short ? "$gmt+00" : "$gmt+00:00";
  1920. }
  1921. $format = $short ? 'H' : 'H:i';
  1922. $abs = abs($offset);
  1923. $seconds = $abs < 100 ? $abs * 3600 : $abs; // detect hours or seconds
  1924. $time = gmdate($format, $seconds);
  1925. return ($offset > 0) ? "$gmt+$time" : "$gmt-$time";
  1926. }
  1927. /**
  1928. * Returns link for URL
  1929. *
  1930. * @param string $text URL
  1931. * @param int $maxlen Max. allowed length
  1932. * @return string
  1933. */
  1934. function cot_build_url($text, $maxlen=64)
  1935. {
  1936. global $sys;
  1937. if (!empty($text))
  1938. {
  1939. if (mb_strpos($text, $sys['scheme'] . '://') !== 0)
  1940. {
  1941. $text = $sys['scheme'] . '://' . $text;
  1942. }
  1943. $text = htmlspecialchars($text);
  1944. $text = cot_rc_link($text, cot_cutstring($text, $maxlen));
  1945. }
  1946. return $text;
  1947. }
  1948. /**
  1949. * Returns link to user profile
  1950. *
  1951. * @param int $id User ID
  1952. * @param string $user User name
  1953. * @param mixed $extra_attrs Extra link tag attributes as a string or associative array,
  1954. * e.g. array('class' => 'usergrp_admin')
  1955. * @return string
  1956. */
  1957. function cot_build_user($id, $user, $extra_attrs = '')
  1958. {
  1959. if (function_exists('cot_build_user_custom'))
  1960. {
  1961. return cot_build_user_custom($id, $user, $extra_attrs);
  1962. }
  1963. if (!$id)
  1964. {
  1965. return empty($user) ? '' : $user;
  1966. }
  1967. else
  1968. {
  1969. return empty($user) ? '?' : cot_rc_link(cot_url('users', 'm=details&id='.$id.'&u='.$user), $user, $extra_attrs);
  1970. }
  1971. }
  1972. /**
  1973. * Displays User full name
  1974. *
  1975. * Format of full name is language specific and defined by $R['users_full_name']
  1976. * resource string.
  1977. *
  1978. * @param array|int $user User Data or User ID
  1979. * @return string
  1980. */
  1981. function cot_user_full_name($user)
  1982. {
  1983. if (empty($user)) return '';
  1984. if(function_exists('cot_user_full_name_custom')) return cot_user_full_name_custom($user);
  1985. if (is_int($user) && $user > 0 || ctype_digit($user)) {
  1986. require_once cot_incfile('users', 'module');
  1987. $user = cot_user_data($user);
  1988. }
  1989. if (empty($user)) return '';
  1990. $user_fname = '';
  1991. if(!empty($user['user_firstname'])) {
  1992. $user_fname = $user['user_firstname'];
  1993. } elseif(!empty($user['user_first_name'])) {
  1994. $user_fname = $user['user_first_name'];
  1995. }
  1996. $user_mname = '';
  1997. if(!empty($user['user_middlename'])) {
  1998. $user_mname = $user['user_middlename'];
  1999. } elseif(!empty($user['user_middle_name'])) {
  2000. $user_mname = $user['user_middle_name'];
  2001. }
  2002. $user_lname = '';
  2003. if(!empty($user['user_lastname'])) {
  2004. $user_lname = $user['user_lastname'];
  2005. } elseif(!empty($user['user_last_name'])) {
  2006. $user_lname = $user['user_last_name'];
  2007. }
  2008. if($user_fname != '' || $user_mname != '' || $user_lname != '') {
  2009. $full_name = trim(
  2010. cot_rc('users_full_name',
  2011. array(
  2012. 'firstname' => $user_fname,
  2013. 'middlename' => $user_mname,
  2014. 'lastname' => $user_lname,
  2015. 'name' => $user['user_name']
  2016. )
  2017. )
  2018. );
  2019. } else {
  2020. $full_name = $user['user_name'];
  2021. }
  2022. return $full_name;
  2023. }
  2024. /**
  2025. * Returns group link (button)
  2026. *
  2027. * @param int $grpid Group ID
  2028. * @param bool $title Return group title instead of name
  2029. * @return string
  2030. */
  2031. function cot_build_group($grpid, $title = false)
  2032. {
  2033. if (empty($grpid))
  2034. return '';
  2035. global $cot_groups, $L;
  2036. $type = ($title) ? 'title' : 'name';
  2037. if ($cot_groups[$grpid]['hidden'])
  2038. {
  2039. if (cot_auth('users', 'a', 'A'))
  2040. {
  2041. return cot_rc_link(cot_url('users', 'gm=' . $grpid), $cot_groups[$grpid][$type] . ' (' . $L['Hidden'] . ')');
  2042. }
  2043. else
  2044. {
  2045. return $L['Hidden'];
  2046. }
  2047. }
  2048. else
  2049. {
  2050. if ($type == 'title' && isset($L['users_grp_' . $grpid . '_title']))
  2051. {
  2052. return cot_rc_link(cot_url('users', 'gm=' . $grpid), $L['users_grp_' . $grpid . '_title']);
  2053. }
  2054. return cot_rc_link(cot_url('users', 'gm=' . $grpid), $cot_groups[$grpid][$type]);
  2055. }
  2056. }
  2057. /**
  2058. * Returns user group icon
  2059. *
  2060. * @param string $src Image file path
  2061. * @return string
  2062. */
  2063. function cot_build_groupicon($src)
  2064. {
  2065. return ($src) ? cot_rc("icon_group", array('src' => $src)) : '';
  2066. }
  2067. /**
  2068. * Returns all user tags for XTemplate
  2069. *
  2070. * @param mixed $user_data User Info Array
  2071. * @param string $tag_prefix Prefix for tags
  2072. * @param string $emptyname Name text if user is not exist
  2073. * @param bool $allgroups Build info about all user groups
  2074. * @param bool $cacheitem Cache tags
  2075. * @return array
  2076. * @global CotDB $db
  2077. */
  2078. function cot_generate_usertags($user_data, $tag_prefix = '', $emptyname='', $allgroups = false, $cacheitem = true)
  2079. {
  2080. global $db, $cot_extrafields, $cot_groups, $cfg, $L, $user_cache, $db_users;
  2081. static $extp_first = null, $extp_main = null;
  2082. $return_array = array();
  2083. if (is_null($extp_first))
  2084. {
  2085. $extp_first = cot_getextplugins('usertags.first');
  2086. $extp_main = cot_getextplugins('usertags.main');
  2087. }
  2088. /* === Hook === */
  2089. foreach ($extp_first as $pl)
  2090. {
  2091. include $pl;
  2092. }
  2093. /* ===== */
  2094. $user_id = is_array($user_data) ? (int)$user_data['user_id'] : (is_numeric($user_data) ? (int)$user_data : 0);
  2095. if (isset($user_cache[$user_id]))
  2096. {
  2097. $temp_array = $user_cache[$user_id];
  2098. }
  2099. else
  2100. {
  2101. if (!is_array($user_data) && $user_id > 0)
  2102. {
  2103. $sql = $db->query("SELECT * FROM $db_users WHERE user_id = $user_id LIMIT 1");
  2104. $user_data = $sql->fetch();
  2105. }
  2106. else if (!is_array($user_data))
  2107. {
  2108. $user_data = array();
  2109. }
  2110. if (is_array($user_data) && $user_data['user_id'] > 0 && !empty($user_data['user_name']))
  2111. {
  2112. $user_data['user_birthdate'] = cot_date2stamp($user_data['user_birthdate']);
  2113. $user_data['user_text'] = cot_parse($user_data['user_text'], $cfg['users']['usertextimg']);
  2114. $temp_array = array(
  2115. 'ID' => $user_data['user_id'],
  2116. 'NAME' => cot_build_user($user_data['user_id'], htmlspecialchars($user_data['user_name'])),
  2117. 'NICKNAME' => htmlspecialchars($user_data['user_name']),
  2118. 'DETAILSLINK' => cot_url('users', 'm=details&id=' . $user_data['user_id'].'&u='.htmlspecialchars($user_data['user_name'])),
  2119. 'DETAILSLINKSHORT' => cot_url('users', 'm=details&id=' . $user_data['user_id']),
  2120. 'FULL_NAME' => htmlspecialchars(cot_user_full_name($user_data)),
  2121. 'TITLE' => $cot_groups[$user_data['user_maingrp']]['title'],
  2122. 'MAINGRP' => cot_build_group($user_data['user_maingrp']),
  2123. 'MAINGRPID' => $user_data['user_maingrp'],
  2124. 'MAINGRPNAME' => $cot_groups[$user_data['user_maingrp']]['name'],
  2125. 'MAINGRPTITLE' => cot_build_group($user_data['user_maingrp'], true),
  2126. 'MAINGRPSTARS' => cot_build_stars($cot_groups[$user_data['user_maingrp']]['level']),
  2127. 'MAINGRPICON' => cot_build_groupicon($cot_groups[$user_data['user_maingrp']]['icon']),
  2128. 'COUNTRY' => cot_build_country($user_data['user_country']),
  2129. 'COUNTRYFLAG' => cot_build_flag($user_data['user_country']),
  2130. 'TEXT' => $user_data['user_text'],
  2131. 'EMAIL' => cot_build_email($user_data['user_email'], $user_data['user_hideemail']),
  2132. 'THEME' => $user_data['user_theme'],
  2133. 'SCHEME' => $user_data['user_scheme'],
  2134. 'LANG' => $user_data['user_lang'],
  2135. 'GENDER' => ($user_data['user_gender'] == '' || $user_data['user_gender'] == 'U') ? '' : $L['Gender_' . $user_data['user_gender']],
  2136. 'BIRTHDATE' => (is_null($user_data['user_birthdate'])) ? '' : cot_date('date_full', $user_data['user_birthdate']),
  2137. 'BIRTHDATE_STAMP' => (is_null($user_data['user_birthdate'])) ? '' : $user_data['user_birthdate'],
  2138. 'AGE' => (is_null($user_data['user_birthdate'])) ? '' : cot_build_age($user_data['user_birthdate']),
  2139. 'TIMEZONE' => cot_build_timezone(cot_timezone_offset($user_data['user_timezone'], false, false)) . ' ' .str_replace('_', ' ', $user_data['user_timezone']),
  2140. 'REGDATE' => cot_date('datetime_medium', $user_data['user_regdate']),
  2141. 'REGDATE_STAMP' => $user_data['user_regdate'],
  2142. 'LASTLOG' => cot_date('datetime_medium', $user_data['user_lastlog']),
  2143. 'LASTLOG_STAMP' => $user_data['user_lastlog'],
  2144. 'LOGCOUNT' => $user_data['user_logcount'],
  2145. 'POSTCOUNT' => !empty($user_data['user_postcount']) ? $user_data['user_postcount'] : 0,
  2146. 'LASTIP' => $user_data['user_lastip']
  2147. );
  2148. if ($allgroups)
  2149. {
  2150. $temp_array['GROUPS'] = cot_build_groupsms($user_data['user_id'], FALSE, $user_data['user_maingrp']);
  2151. }
  2152. // Extra fields
  2153. if (!empty(cot::$extrafields[cot::$db->users])) {
  2154. foreach (cot::$extrafields[cot::$db->users] as $exfld) {
  2155. $exfld_title = cot_extrafield_title($exfld, 'user_');
  2156. $temp_array[strtoupper($exfld['field_name'])] = cot_build_extrafields_data('user', $exfld, $user_data['user_' . $exfld['field_name']]);
  2157. $temp_array[strtoupper($exfld['field_name']) . '_TITLE'] = $exfld_title;
  2158. $temp_array[strtoupper($exfld['field_name']) . '_VALUE'] = $user_data['user_' . $exfld['field_name']];
  2159. }
  2160. }
  2161. }
  2162. else
  2163. {
  2164. $temp_array = array(
  2165. 'ID' => 0,
  2166. 'NAME' => (!empty($emptyname)) ? $emptyname : $L['Deleted'],
  2167. 'NICKNAME' => (!empty($emptyname)) ? $emptyname : $L['Deleted'],
  2168. 'FULL_NAME' => (!empty($emptyname)) ? $emptyname : $L['Deleted'],
  2169. 'MAINGRP' => cot_build_group(1),
  2170. 'MAINGRPID' => 1,
  2171. 'MAINGRPSTARS' => '',
  2172. 'MAINGRPICON' => cot_build_groupicon($cot_groups[1]['icon']),
  2173. 'COUNTRY' => cot_build_country(''),
  2174. 'COUNTRYFLAG' => cot_build_flag(''),
  2175. 'TEXT' => '',
  2176. 'EMAIL' => '',
  2177. 'GENDER' => '',
  2178. 'BIRTHDATE' => '',
  2179. 'BIRTHDATE_STAMP' => '',
  2180. 'AGE' => '',
  2181. 'REGDATE' => '',
  2182. 'REGDATE_STAMP' => '',
  2183. 'POSTCOUNT' => '',
  2184. 'LASTIP' => ''
  2185. );
  2186. }
  2187. /* === Hook === */
  2188. foreach ($extp_main as $pl)
  2189. {
  2190. include $pl;
  2191. }
  2192. /* ===== */
  2193. if(is_array($user_data) && isset($user_data['user_id'])) {
  2194. $cacheitem && $user_cache[$user_data['user_id']] = $temp_array;
  2195. }
  2196. }
  2197. foreach ($temp_array as $key => $val)
  2198. {
  2199. $return_array[$tag_prefix . $key] = $val;
  2200. }
  2201. return $return_array;
  2202. }
  2203. /**
  2204. * Resize an image
  2205. *
  2206. * @param string $source Original image path.
  2207. * @param string $target Target path for saving, or 'return' to return the resized image data directly.
  2208. * @param int $target_width Maximum width of resized image.
  2209. * @param int $target_height Maximum height of resized image.
  2210. * @param string $crop Crop the image to a certain ratio. Set to 'fit' to calculate ratio from target width and height.
  2211. * @param string $fillcolor Color fill a transparent gif or png.
  2212. * @param int $quality JPEG quality
  2213. * @param bool $sharpen Sharpen JPEG image after resize.
  2214. * @return mixed Boolean or image resource, depending on $target
  2215. */
  2216. function cot_imageresize($source, $target='return', $target_width=99999, $target_height=99999, $crop='', $fillcolor='', $quality=90, $sharpen=true)
  2217. {
  2218. if (!file_exists($source)) return false;
  2219. $source_size = getimagesize($source);
  2220. if(!$source_size) return false;
  2221. $mimetype = $source_size['mime'];
  2222. if (substr($mimetype, 0, 6) != 'image/') return false;
  2223. $source_width = $source_size[0];
  2224. $source_height = $source_size[1];
  2225. if($target_width > $source_width) $target_width = $source_width; $noscaling_x = true;
  2226. if($target_height > $source_height) $target_height = $source_height; $noscaling_y = true;
  2227. $fillcolor = preg_replace('/[^0-9a-fA-F]/', '', (string)$fillcolor);
  2228. if (!$fillcolor && $noscaling_x && $noscaling_y)
  2229. {
  2230. $data = file_get_contents($source);
  2231. if($target == 'return') return $data;
  2232. }
  2233. $offsetX = 0;
  2234. $offsetY = 0;
  2235. if($crop)
  2236. {
  2237. $crop = ($crop == 'fit') ? array($target_width, $target_height) : explode(':', (string)$crop);
  2238. if(count($crop) == 2)
  2239. {
  2240. $source_ratio = $source_width / $source_height;
  2241. $target_ratio = (float)$crop[0] / (float)$crop[1];
  2242. if ($source_ratio < $target_ratio)
  2243. {
  2244. $temp = $source_height;
  2245. $source_height = $source_width / $target_ratio;
  2246. $offsetY = ($temp - $source_height) / 2;
  2247. }
  2248. if ($source_ratio > $target_ratio)
  2249. {
  2250. $temp = $source_width;
  2251. $source_width = $source_height * $target_ratio;
  2252. $offsetX = ($temp - $source_width) / 2;
  2253. }
  2254. }
  2255. }
  2256. $width_ratio = $target_width / $source_width;
  2257. $height_ratio = $target_height / $source_height;
  2258. if ($width_ratio * $source_height < $target_height)
  2259. {
  2260. $target_height = ceil($width_ratio * $source_height);
  2261. }
  2262. else
  2263. {
  2264. $target_width = ceil($height_ratio * $source_width);
  2265. }
  2266. // Avoid loading images there's not enough memory for
  2267. if (!cot_img_check_memory($source, (int)ceil($target_width * $target_height * 4 / 1048576))){
  2268. return false;
  2269. }
  2270. $canvas = imagecreatetruecolor($target_width, $target_height);
  2271. switch($mimetype)
  2272. {
  2273. case 'image/gif':
  2274. $fn_create = 'imagecreatefromgif';
  2275. $fn_output = 'imagegif';
  2276. $mimetype = 'image/gif';
  2277. //$quality = round(10 - ($quality / 10));
  2278. $sharpen = false;
  2279. break;
  2280. case 'image/x-png':
  2281. case 'image/png':
  2282. $fn_create = 'imagecreatefrompng';
  2283. $fn_output = 'imagepng';
  2284. $quality = round(10 - ($quality / 10));
  2285. $sharpen = false;
  2286. break;
  2287. default:
  2288. $fn_create = 'imagecreatefromjpeg';
  2289. $fn_output = 'imagejpeg';
  2290. $sharpen = ($target_width < 75 || $target_height < 75) ? false : $sharpen;
  2291. break;
  2292. }
  2293. $source_data = $fn_create($source);
  2294. if (in_array($mimetype, array('image/gif', 'image/png')))
  2295. {
  2296. if (!$fillcolor)
  2297. {
  2298. imagealphablending($canvas, false);
  2299. imagesavealpha($canvas, true);
  2300. }
  2301. elseif(strlen($fillcolor) == 6 || strlen($fillcolor) == 3)
  2302. {
  2303. $background = (strlen($fillcolor) == 6) ?
  2304. imagecolorallocate($canvas, hexdec($fillcolor[0].$fillcolor[1]), hexdec($fillcolor[2].$fillcolor[3]), hexdec($fillcolor[4].$fillcolor[5])):
  2305. imagecolorallocate($canvas, hexdec($fillcolor[0].$fillcolor[0]), hexdec($fillcolor[1].$fillcolor[1]), hexdec($fillcolor[2].$fillcolor[2]));
  2306. imagefill($canvas, 0, 0, $background);
  2307. }
  2308. }
  2309. imagecopyresampled($canvas, $source_data, 0, 0, $offsetX, $offsetY, $target_width, $target_height, $source_width, $source_height);
  2310. imagedestroy($source_data);
  2311. $canvas = ($sharpen) ? cot_imagesharpen($canvas, $source_width, $target_width) : $canvas;
  2312. if($target == 'return')
  2313. {
  2314. ob_start();
  2315. $fn_output($canvas, null, $quality);
  2316. $data = ob_get_contents();
  2317. ob_end_clean();
  2318. imagedestroy($canvas);
  2319. return $data;
  2320. }
  2321. else
  2322. {
  2323. $result = $fn_output($canvas, $target, $quality);
  2324. imagedestroy($canvas);
  2325. return $result;
  2326. }
  2327. }
  2328. // Fix for non-bundled GD versions
  2329. // Made by Chao Xu(Mgccl) 2/28/07
  2330. // www.webdevlogs.com
  2331. // V 1.0
  2332. if (!function_exists('imageconvolution'))
  2333. {
  2334. function imageconvolution($src, $filter, $filter_div, $offset)
  2335. {
  2336. if ($src == NULL)
  2337. {
  2338. return 0;
  2339. }
  2340. $sx = imagesx($src);
  2341. $sy = imagesy($src);
  2342. $srcback = imagecreatetruecolor($sx, $sy);
  2343. imagecopy($srcback, $src, 0, 0, 0, 0, $sx, $sy);
  2344. if ($srcback == NULL)
  2345. {
  2346. return 0;
  2347. }
  2348. // Fix here
  2349. // $pxl array was the problem so simply set it with very low values
  2350. $pxl = array(1, 1);
  2351. // this little fix worked for me as the undefined array threw out errors
  2352. for ($y = 0; $y < $sy; ++$y)
  2353. {
  2354. for ($x = 0; $x < $sx; ++$x)
  2355. {
  2356. $new_r = $new_g = $new_b = 0;
  2357. $alpha = imagecolorat($srcback, $pxl[0], $pxl[1]);
  2358. $new_a = $alpha >> 24;
  2359. for ($j = 0; $j < 3; ++$j)
  2360. {
  2361. $yv = min(max($y - 1 + $j, 0), $sy - 1);
  2362. for ($i = 0; $i < 3; ++$i)
  2363. {
  2364. $pxl = array(min(max($x - 1 + $i, 0), $sx - 1), $yv);
  2365. $rgb = imagecolorat($srcback, $pxl[0], $pxl[1]);
  2366. $new_r += ( ($rgb >> 16) & 0xFF) * $filter[$j][$i];
  2367. $new_g += ( ($rgb >> 8) & 0xFF) * $filter[$j][$i];
  2368. $new_b += ( $rgb & 0xFF) * $filter[$j][$i];
  2369. }
  2370. }
  2371. $new_r = ($new_r / $filter_div) + $offset;
  2372. $new_g = ($new_g / $filter_div) + $offset;
  2373. $new_b = ($new_b / $filter_div) + $offset;
  2374. $new_r = ($new_r > 255) ? 255 : (($new_r < 0) ? 0 : $new_r);
  2375. $new_g = ($new_g > 255) ? 255 : (($new_g < 0) ? 0 : $new_g);
  2376. $new_b = ($new_b > 255) ? 255 : (($new_b < 0) ? 0 : $new_b);
  2377. $new_pxl = imagecolorallocatealpha($src, (int) $new_r, (int) $new_g, (int) $new_b, $new_a);
  2378. if ($new_pxl == -1)
  2379. {
  2380. $new_pxl = imagecolorclosestalpha($src, (int) $new_r, (int) $new_g, (int) $new_b, $new_a);
  2381. }
  2382. if (($y >= 0) && ($y < $sy))
  2383. {
  2384. imagesetpixel($src, $x, $y, $new_pxl);
  2385. }
  2386. }
  2387. }
  2388. imagedestroy($srcback);
  2389. return 1;
  2390. }
  2391. }
  2392. /**
  2393. * Sharpen an image after resize
  2394. *
  2395. * @param image resource $imgdata Image resource from an image creation function
  2396. * @param int $source_width Width of image before resize
  2397. * @param int $target_width Width of image to sharpen (after resize)
  2398. * @return resource - image resource
  2399. */
  2400. function cot_imagesharpen($imgdata, $source_width, $target_width)
  2401. {
  2402. $s = $target_width * (750.0 / $source_width);
  2403. $a = 52;
  2404. $b = -0.27810650887573124;
  2405. $c = .00047337278106508946;
  2406. $sharpness = max(round($a+$b*$s+$c*$s*$s), 0);
  2407. $sharpenmatrix = array(
  2408. array(-1, -2, -1),
  2409. array(-2, $sharpness + 12, -2),
  2410. array(-1, -2, -1)
  2411. );
  2412. imageconvolution($imgdata, $sharpenmatrix, $sharpness, 0);
  2413. return $imgdata;
  2414. }
  2415. /**
  2416. * Checks if PHP can have enough memory to process an image
  2417. *
  2418. * @param string $file_path Path to an image
  2419. * @param int $extra_size Extra size to adjust to the estimate (in MB)
  2420. * @return boolean TRUE if enough memory is available, FALSE otherwise
  2421. */
  2422. function cot_img_check_memory($file_path, $extra_size = 0)
  2423. {
  2424. // Gettimg memory size required to process the image
  2425. $source_size = getimagesize($file_path);
  2426. $tweekfactor = 1.2;
  2427. $K64 = 65536; // number of bytes in 64K
  2428. $MB15 = 15 * 1048576; // 15 Mb
  2429. // Wrong image
  2430. if (!$source_size) return false;
  2431. $width_orig = $source_size[0];
  2432. $height_orig = $source_size[1];
  2433. $depth_orig = ($source_size['bits'] > 8) ? ($source_size['bits'] / 8) : 1;
  2434. $channels_orig = $source_size['channels'] > 0 ? $source_size['channels'] : 4;
  2435. // In Bytes
  2436. $needMem = $width_orig * $height_orig * $depth_orig * $channels_orig + $K64;
  2437. // Adding some offset memory for other image processing and script variables,
  2438. // otherwise the script fails
  2439. $tweekSize = $needMem * $tweekfactor;
  2440. $tweekSize = $tweekSize < $MB15 ? $MB15 : $tweekSize;
  2441. $needMem = round($needMem + $tweekSize + ($extra_size * 1048576) + filesize($file_path));
  2442. // Trying to allocate memory required
  2443. return cot_memory_allocate($needMem);
  2444. }
  2445. /**
  2446. * Returns themes info data for all available themes or a specified one
  2447. *
  2448. * @param string $theme_name Name of theme to get info.
  2449. * Returns list for all themes if no name specified.
  2450. * @return mixed Array of Theme info data or Theme info data or FALSE
  2451. */
  2452. function cot_themes_info($theme_name = null)
  2453. {
  2454. require_once cot_incfile('extensions');
  2455. $themes_data = array();
  2456. $themelist = array();
  2457. $handle = opendir(cot::$cfg['themes_dir']);
  2458. while ($f = readdir($handle)) {
  2459. if (mb_strpos($f, '.') === FALSE && is_dir(cot::$cfg['themes_dir'] . "/$f") && $f != "admin") {
  2460. $themelist[] = $f;
  2461. }
  2462. }
  2463. closedir($handle);
  2464. if (!is_null($theme_name)) {
  2465. if (!in_array($theme_name, $themelist)) {
  2466. return false;
  2467. } else {
  2468. $themelist = array($theme_name);
  2469. }
  2470. } else {
  2471. sort($themelist);
  2472. }
  2473. foreach ($themelist as $name) {
  2474. if ($theme_name && $theme_name != $name) continue;
  2475. $themeinfo = array();
  2476. $themeinfo_file = cot::$cfg['themes_dir'] . "/$name/$name.php";
  2477. if (file_exists($themeinfo_file) && $info = cot_infoget($themeinfo_file, 'COT_THEME')) {
  2478. $themeinfo = $info;
  2479. if (empty($themeinfo['Title'])) {
  2480. $themeinfo['Title'] = isset($info['Name']) ? $info['Name'] : $name;
  2481. }
  2482. $schemes_list = array();
  2483. if (!empty($info['Schemes'])) {
  2484. $schemes = preg_split('/\s*,\s*/', $info['Schemes']);
  2485. sort($schemes);
  2486. foreach ($schemes as $scheme) {
  2487. list($sc_name, $sc_title) = explode(':', $scheme);
  2488. $schemes_list[$sc_name] = $sc_title;
  2489. }
  2490. }
  2491. $themeinfo['Schemes'] = $schemes_list;
  2492. if(!isset($themeinfo['Version'])) $themeinfo['Version'] = '';
  2493. }
  2494. if (sizeof($themeinfo) > 0) $themes_data[$name] = $themeinfo;
  2495. }
  2496. if (is_null($theme_name)) {
  2497. return $themes_data;
  2498. } else {
  2499. return $themes_data[$theme_name];
  2500. }
  2501. }
  2502. /**
  2503. * Returns Theme/Scheme selection dropdown
  2504. *
  2505. * @param string $selected_theme Seleced theme
  2506. * @param string $selected_scheme Seleced color scheme
  2507. * @param string $title Dropdown name
  2508. * @return string
  2509. */
  2510. function cot_selectbox_theme($selected_theme, $selected_scheme, $input_name)
  2511. {
  2512. $themes_info = cot_themes_info();
  2513. $values = array();
  2514. $titles = array();
  2515. foreach ($themes_info as $name => $info) {
  2516. if ($info) {
  2517. $version = $info['Version'];
  2518. $title = $info['Title'] . ($version ? " v$version" : '');
  2519. if (sizeof($info['Schemes'])) {
  2520. foreach ($info['Schemes'] as $sc_name => $sc_title) {
  2521. $values[] = $name . ':' . $sc_name;
  2522. $titles[] = count($info['Schemes']) > 1 ? $title . ' (' . $sc_title . ')' : $title;
  2523. }
  2524. } else {
  2525. $values[] = "$name:default";
  2526. $titles[] = $title;
  2527. }
  2528. }
  2529. }
  2530. return cot_selectbox("$selected_theme:$selected_scheme", $input_name, $values, $titles, false);
  2531. }
  2532. /*
  2533. * ======================== Error & Message + Logs API ========================
  2534. */
  2535. /**
  2536. * If condition is true, triggers an error with given message and source
  2537. *
  2538. * @param bool $condition Boolean condition
  2539. * @param string $message Error message or message key
  2540. * @param string $src Error source field name
  2541. */
  2542. function cot_check($condition, $message, $src = 'default')
  2543. {
  2544. if ($condition)
  2545. {
  2546. cot_error($message, $src);
  2547. }
  2548. }
  2549. /**
  2550. * Checks if there are messages to display
  2551. *
  2552. * @param string $src If non-emtpy, check messages in this specific source only
  2553. * @param string $class If non-empty, check messages of this specific class only
  2554. * @return bool
  2555. */
  2556. function cot_check_messages($src = '', $class = '')
  2557. {
  2558. global $error_string, $sys;
  2559. if (empty($_SESSION['cot_messages'][cot::$sys['site_id']])) return false;
  2560. if (empty($src) && empty($class)) {
  2561. return (is_array($_SESSION['cot_messages'][cot::$sys['site_id']]) && count($_SESSION['cot_messages'][cot::$sys['site_id']]) > 0)
  2562. || !empty($error_string);
  2563. }
  2564. if (!is_array($_SESSION['cot_messages'][cot::$sys['site_id']])) return false;
  2565. if (empty($src))
  2566. {
  2567. foreach ($_SESSION['cot_messages'][cot::$sys['site_id']] as $src => $grp)
  2568. {
  2569. foreach ($grp as $msg)
  2570. {
  2571. if ($msg['class'] == $class)
  2572. {
  2573. return true;
  2574. }
  2575. }
  2576. }
  2577. }
  2578. elseif (empty($class))
  2579. {
  2580. return count($_SESSION['cot_messages'][cot::$sys['site_id']][$src]) > 0;
  2581. }
  2582. else
  2583. {
  2584. foreach ($_SESSION['cot_messages'][cot::$sys['site_id']][$src] as $msg)
  2585. {
  2586. if ($msg['class'] == $class)
  2587. {
  2588. return true;
  2589. }
  2590. }
  2591. }
  2592. return false;
  2593. }
  2594. /**
  2595. * Clears error and other messages after they have bin displayed
  2596. * @param string $src If non-emtpy, clear messages in this specific source only
  2597. * @param string $class If non-empty, clear messages of this specific class only
  2598. * @see cot_error()
  2599. * @see cot_message()
  2600. */
  2601. function cot_clear_messages($src = '', $class = '')
  2602. {
  2603. global $error_string, $sys;
  2604. if (empty($src) && empty($class)) {
  2605. unset($_SESSION['cot_messages'][cot::$sys['site_id']]);
  2606. unset($error_string);
  2607. }
  2608. if(empty($_SESSION['cot_messages'][cot::$sys['site_id']])) return;
  2609. if (!is_array($_SESSION['cot_messages'][cot::$sys['site_id']]) || (!empty($src) && !is_array($_SESSION['cot_messages'][cot::$sys['site_id']][$src]))) {
  2610. return;
  2611. }
  2612. if (empty($src)) {
  2613. foreach ($_SESSION['cot_messages'][cot::$sys['site_id']] as $src => $grp) {
  2614. $new_grp = array();
  2615. foreach ($grp as $msg){
  2616. if ($msg['class'] != $class) {
  2617. $new_grp[] = $msg;
  2618. }
  2619. }
  2620. if (count($new_grp) > 0) {
  2621. $_SESSION['cot_messages'][cot::$sys['site_id']][$src] = $new_grp;
  2622. } else {
  2623. unset($_SESSION['cot_messages'][cot::$sys['site_id']][$src]);
  2624. }
  2625. }
  2626. } elseif (empty($class)) {
  2627. unset($_SESSION['cot_messages'][cot::$sys['site_id']][$src]);
  2628. } else {
  2629. $new_grp = array();
  2630. foreach ($_SESSION['cot_messages'][cot::$sys['site_id']][$src] as $msg) {
  2631. if ($msg['class'] != $class) {
  2632. $new_grp[] = $msg;
  2633. }
  2634. }
  2635. if (count($new_grp) > 0) {
  2636. $_SESSION['cot_messages'][cot::$sys['site_id']][$src] = $new_grp;
  2637. } else {
  2638. unset($_SESSION['cot_messages'][cot::$sys['site_id']][$src]);
  2639. }
  2640. }
  2641. }
  2642. /**
  2643. * Terminates script execution and performs redirect
  2644. *
  2645. * @param bool $cond Really die?
  2646. * @param bool $notfound Page not found?
  2647. * @return bool
  2648. */
  2649. function cot_die($cond = true, $notfound = false)
  2650. {
  2651. if ($cond)
  2652. {
  2653. $msg = $notfound ? '404' : '950';
  2654. cot_die_message($msg, true);
  2655. }
  2656. return FALSE;
  2657. }
  2658. /**
  2659. * Terminates script execution with fatal error
  2660. *
  2661. * @param string $text Reason
  2662. * @param string $title Message title
  2663. */
  2664. function cot_diefatal($text='Reason is unknown.', $title='Fatal error')
  2665. {
  2666. // cot class can be not initialised yet
  2667. global $cfg;
  2668. if ($cfg['display_errors']) {
  2669. $mainTitle = isset($cfg['maintitle']) ? $cfg['maintitle'] : $cfg['mainurl'];
  2670. $message_body = '<p><em>'.@date('Y-m-d H:i').'</em></p>';
  2671. $message_body .= '<p>'.$text.'</p>';
  2672. ob_clean();
  2673. debug_print_backtrace();
  2674. $backtrace = ob_get_contents();
  2675. ob_clean();
  2676. $message_body .= '<pre style="overflow:auto">'.$backtrace.'</pre>';
  2677. $message_body .= '<hr /><a href="'.$cfg['mainurl'].'">'.$mainTitle.'</a>';
  2678. cot_die_message(500, true, $title, $message_body);
  2679. } else {
  2680. $backtrace = debug_backtrace();
  2681. if (isset($backtrace[1])) {
  2682. $text .= ' in file ' . $backtrace[1]['file'] . ' at line ' . $backtrace[1]['line'] . ' function ' . $backtrace[1]['function'] . '(' . implode(', ', $backtrace[1]['args']) . ')';
  2683. }
  2684. error_log("$title: $text");
  2685. cot_die_message(503, true);
  2686. }
  2687. }
  2688. /**
  2689. * Terminates script execution and displays message page
  2690. *
  2691. * @param integer $code Message code
  2692. * @param boolean $header Render page header
  2693. * @param string $message_title Custom page title
  2694. * @param string $message_body Custom message body
  2695. * @param string $redirect Optional URL to redirect after 3 seconds
  2696. */
  2697. function cot_die_message($code, $header = TRUE, $message_title = '', $message_body = '', $redirect = '')
  2698. {
  2699. // Globals and requirements
  2700. // cot class can be not initialised yet
  2701. global $error_string, $out, $L, $R, $cfg;
  2702. $LL = is_array($L) ? $L : array();
  2703. require_once cot_langfile('message', 'core');
  2704. $L = array_merge($L, $LL);
  2705. if (cot_error_found() && $_SERVER['REQUEST_METHOD'] == 'POST') {
  2706. // Save the POST data
  2707. cot_import_buffer_save();
  2708. if (!empty($error_string)) {
  2709. // Message should not be lost
  2710. cot_error($error_string);
  2711. }
  2712. }
  2713. // Determine response header
  2714. static $msg_status = array(
  2715. 100 => '403 Forbidden',
  2716. 101 => '200 OK',
  2717. 102 => '200 OK',
  2718. 105 => '200 OK',
  2719. 106 => '200 OK',
  2720. 109 => '200 OK',
  2721. 117 => '403 Forbidden',
  2722. 118 => '200 OK',
  2723. 151 => '403 Forbidden',
  2724. 152 => '403 Forbidden',
  2725. 153 => '403 Forbidden',
  2726. 157 => '403 Forbidden',
  2727. 300 => '200 OK',
  2728. 400 => '400 Bad Request',
  2729. 401 => '401 Authorization Required',
  2730. 403 => '403 Forbidden',
  2731. 404 => '404 Not Found',
  2732. 500 => '500 Internal Server Error',
  2733. 503 => '503 Service Unavailable',
  2734. 602 => '403 Forbidden',
  2735. 603 => '403 Forbidden',
  2736. 900 => '503 Service Unavailable',
  2737. 904 => '403 Forbidden',
  2738. 907 => '404 Not Found',
  2739. 911 => '404 Not Found',
  2740. 915 => '200 OK',
  2741. 916 => '200 OK',
  2742. 920 => '200 OK',
  2743. 930 => '403 Forbidden',
  2744. 940 => '403 Forbidden',
  2745. 950 => '403 Forbidden',
  2746. 951 => '503 Service Unavailable'
  2747. );
  2748. if (empty($out['meta_contenttype'])) {
  2749. $out['meta_contenttype'] = 'text/html';
  2750. }
  2751. cot_sendheaders($out['meta_contenttype'], $msg_status[$code]);
  2752. // Determine message title and body
  2753. $title = empty($message_title) ? $L['msg' . $code . '_title'] : $message_title;
  2754. $body = empty($message_body) ? $L['msg' . $code . '_body'] : $message_body;
  2755. // Render the message page
  2756. $tpl_type = defined('COT_ADMIN') ? 'core' : 'module';
  2757. $tpl_path = '';
  2758. $stylesheet = file_exists(cot_schemefile()) ? '<link rel="stylesheet" type="text/css" href="'.cot_schemefile().'"/>' : '';
  2759. $redirect_meta = '';
  2760. if (!empty($redirect)) {
  2761. if (cot_url_check($redirect)) {
  2762. $redirect_meta = '<meta http-equiv="refresh" content="3; url='.$redirect.'" />';
  2763. }
  2764. }
  2765. if (!isset($R['code_basehref'])) {
  2766. // Resource strings
  2767. include $cfg['system_dir'].'/resources.rc.php';
  2768. }
  2769. if ($header) {
  2770. $tpl_path = cot_tplfile("error.$code", $tpl_type);
  2771. if ($tpl_path) {
  2772. $header = false;
  2773. } else {
  2774. echo '<html><head><title>'.$title.'</title><meta name="robots" content="noindex" />'.$R['code_basehref'].$stylesheet.$redirect_meta.'</head><body><div class="block">';
  2775. }
  2776. }
  2777. if (empty($tpl_path)) {
  2778. $tpl_path = cot_tplfile('message', $tpl_type);
  2779. }
  2780. if (empty($tpl_path) || !file_exists($tpl_path)) {
  2781. echo $body;
  2782. } else {
  2783. $t = new XTemplate($tpl_path);
  2784. $t->assign(array(
  2785. 'AJAX_MODE' => defined('COT_AJAX') && COT_AJAX,
  2786. 'MESSAGE_BASEHREF' => $R['code_basehref'],
  2787. 'MESSAGE_STYLESHEET' => $stylesheet,
  2788. 'MESSAGE_REDIRECT' => $redirect_meta,
  2789. 'MESSAGE_TITLE' => $title,
  2790. 'MESSAGE_BODY' => $body
  2791. ));
  2792. $t->parse('MAIN');
  2793. $t->out('MAIN');
  2794. }
  2795. if ($header) {
  2796. echo '</div></body></html>';
  2797. }
  2798. /* === Hook === */
  2799. foreach (cot_getextplugins('die.message') as $pl)
  2800. {
  2801. include $pl;
  2802. }
  2803. /* ===== */
  2804. exit;
  2805. }
  2806. /**
  2807. * Renders different messages on page
  2808. *
  2809. * @param XTemplate $tpl Current template object reference
  2810. * @param string $block Current template block
  2811. */
  2812. function cot_display_messages($tpl, $block = 'MAIN')
  2813. {
  2814. global $L;
  2815. if (!cot_check_messages())
  2816. {
  2817. return;
  2818. }
  2819. $block = (!empty($block)) ? $block.'.' : '';
  2820. $errors = cot_get_messages('', 'error');
  2821. if (count($errors) > 0)
  2822. {
  2823. foreach ($errors as $msg)
  2824. {
  2825. $text = isset($L[$msg['text']]) ? $L[$msg['text']] : $msg['text'];
  2826. $tpl->assign('ERROR_ROW_MSG', $text);
  2827. $tpl->parse($block.'ERROR.ERROR_ROW');
  2828. }
  2829. $tpl->parse($block.'ERROR');
  2830. }
  2831. $warnings = cot_get_messages('', 'warning');
  2832. if (count($warnings) > 0)
  2833. {
  2834. foreach ($warnings as $msg)
  2835. {
  2836. $text = isset($L[$msg['text']]) ? $L[$msg['text']] : $msg['text'];
  2837. $tpl->assign('WARNING_ROW_MSG', $text);
  2838. $tpl->parse($block.'WARNING.WARNING_ROW');
  2839. }
  2840. $tpl->parse($block.'WARNING');
  2841. }
  2842. $okays = cot_get_messages('', 'ok');
  2843. if (count($okays) > 0)
  2844. {
  2845. foreach ($okays as $msg)
  2846. {
  2847. $text = isset($L[$msg['text']]) ? $L[$msg['text']] : $msg['text'];
  2848. $tpl->assign('DONE_ROW_MSG', $text);
  2849. $tpl->parse($block.'DONE.DONE_ROW');
  2850. }
  2851. $tpl->parse($block.'DONE');
  2852. }
  2853. cot_clear_messages();
  2854. }
  2855. /**
  2856. * Records an error message to be displayed on results page
  2857. *
  2858. * @global int $cot_error Global error counter
  2859. * @param string $message Message lang string code or full text
  2860. * @param string $src Error source identifier, such as field name for invalid input
  2861. * @see cot_message()
  2862. */
  2863. function cot_error($message, $src = 'default')
  2864. {
  2865. global $cot_error;
  2866. $cot_error ? $cot_error++ : $cot_error = 1;
  2867. cot_message($message, 'error', $src);
  2868. }
  2869. /**
  2870. * Checks if any errors have been previously detected during current script execution
  2871. *
  2872. * @global int $cot_error Global error counter
  2873. * @global string $error_string Obsolete error message container string
  2874. * @return bool TRUE if any errors were found, FALSE otherwise
  2875. */
  2876. function cot_error_found()
  2877. {
  2878. global $cot_error, $error_string;
  2879. return (bool) $cot_error || !empty($error_string);
  2880. }
  2881. /**
  2882. * Returns an array of messages for a specific source and/or class
  2883. *
  2884. * @param string $src Message source identifier. Search in all sources if empty
  2885. * @param string $class Message class. Search for all classes if empty
  2886. * @return array Array of message strings
  2887. */
  2888. function cot_get_messages($src = 'default', $class = '')
  2889. {
  2890. global $sys;
  2891. $messages = array();
  2892. if (empty($src) && empty($class)) {
  2893. return $_SESSION['cot_messages'][$sys['site_id']];
  2894. }
  2895. if (!isset($_SESSION['cot_messages'][$sys['site_id']]) || !is_array($_SESSION['cot_messages'][$sys['site_id']])) {
  2896. return $messages;
  2897. }
  2898. if (empty($src)) {
  2899. foreach ($_SESSION['cot_messages'][$sys['site_id']] as $src => $grp) {
  2900. foreach ($grp as $msg) {
  2901. if (!empty($class) && $msg['class'] != $class) {
  2902. continue;
  2903. }
  2904. $messages[] = $msg;
  2905. }
  2906. }
  2907. } elseif (isset($_SESSION['cot_messages'][$sys['site_id']][$src]) && is_array($_SESSION['cot_messages'][$sys['site_id']][$src])) {
  2908. if (empty($class)) {
  2909. return $_SESSION['cot_messages'][$sys['site_id']][$src];
  2910. } else {
  2911. foreach ($_SESSION['cot_messages'][$sys['site_id']][$src] as $msg) {
  2912. if ($msg['class'] != $class) {
  2913. continue;
  2914. }
  2915. $messages[] = $msg;
  2916. }
  2917. }
  2918. }
  2919. return $messages;
  2920. }
  2921. /**
  2922. * Collects all messages and implodes them into a single string
  2923. * @param string $src Origin of the target messages
  2924. * @param string $class Group messages of selected class only. Empty to group all
  2925. * @return string Composite HTML string
  2926. * @see cot_error()
  2927. * @see cot_get_messages()
  2928. * @see cot_message()
  2929. */
  2930. function cot_implode_messages($src = 'default', $class = '')
  2931. {
  2932. global $R, $L, $error_string, $sys;
  2933. $res = '';
  2934. if(empty($_SESSION['cot_messages'][cot::$sys['site_id']])) return '';
  2935. if (!is_array($_SESSION['cot_messages'][cot::$sys['site_id']])) return '';
  2936. $messages = cot_get_messages($src, $class);
  2937. foreach ($messages as $msg) {
  2938. $text = isset(cot::$L[$msg['text']]) ? cot::$L[$msg['text']] : $msg['text'];
  2939. $res .= cot_rc('code_msg_line', array('class' => $msg['class'], 'text' => $text));
  2940. }
  2941. if (!empty($error_string) && (empty($class) || $class == 'error')) {
  2942. $res .= cot_rc('code_msg_line', array('class' => 'error', 'text' => $error_string));
  2943. }
  2944. return empty($res) ? '' : cot_rc('code_msg_begin', array('class' => empty($class) ? 'message' : $class))
  2945. . $res . cot::$R['code_msg_end'];
  2946. }
  2947. /**
  2948. * Logs an event
  2949. *
  2950. * @param string $text Event description
  2951. * @param string $group Event group
  2952. * @global CotDB $db
  2953. */
  2954. function cot_log($text, $group = 'def')
  2955. {
  2956. $text = cot_cutstring($text.' - '.$_SERVER['REQUEST_URI'], 255);
  2957. cot::$db && cot::$db->insert(cot::$db->logger, array(
  2958. 'log_date' => (int)cot::$sys['now'],
  2959. 'log_ip' => (!empty(cot::$usr['ip'])) ? cot::$usr['ip'] : '',
  2960. 'log_name' => (!empty(cot::$usr['name']) || cot::$usr['name'] == '0') ? cot::$usr['name'] : '',
  2961. 'log_group' => (!empty($group) || $group == '0') ? $group : '',
  2962. 'log_text' => $text
  2963. ));
  2964. }
  2965. /**
  2966. * Logs wrong input
  2967. *
  2968. * @param string $s Source type
  2969. * @param string $e Filter type
  2970. * @param string $v Variable name
  2971. * @param string $o Value
  2972. */
  2973. function cot_log_import($s, $e, $v, $o)
  2974. {
  2975. if ($e == 'PSW') $o = str_repeat('*', mb_strlen($o));
  2976. $text = "A variable type check failed, expecting ".$s."/".$e." for '".$v."' : ".$o;
  2977. cot_log($text, 'sec');
  2978. }
  2979. /**
  2980. * Records a generic message to be displayed on results page
  2981. * @param string $text Message lang string code or full text
  2982. * @param string $class Message class: 'error', 'ok', 'warning'
  2983. * @param string $src Message source identifier
  2984. * @see cot_error()
  2985. */
  2986. function cot_message($text, $class = 'ok', $src = 'default')
  2987. {
  2988. global $cfg, $sys;
  2989. $msgSeparate = isset(cot::$cfg['msg_separate']) ? cot::$cfg['msg_separate'] : false;
  2990. if (!$msgSeparate) {
  2991. // Force the src to default if all errors are displayed in the same place
  2992. $src = 'default';
  2993. }
  2994. $_SESSION['cot_messages'][cot::$sys['site_id']][$src][] = array(
  2995. 'text' => $text,
  2996. 'class' => $class
  2997. );
  2998. }
  2999. /*
  3000. * =============================== File Path Functions ========================
  3001. */
  3002. /**
  3003. * Returns path to include file
  3004. *
  3005. * @param string $name Extension or API name
  3006. * @param string $type Extension type: 'module', 'plug' or 'core' for core API
  3007. * @param string $part Name of the extension part
  3008. * @return string File path
  3009. */
  3010. function cot_incfile($name, $type = 'core', $part = 'functions')
  3011. {
  3012. global $cfg;
  3013. if ($type == 'core')
  3014. {
  3015. return $cfg['system_dir'] . "/$name.php";
  3016. }
  3017. elseif ($type == 'plug')
  3018. {
  3019. return $cfg['plugins_dir']."/$name/inc/$name.$part.php";
  3020. }
  3021. elseif ($name == 'admin')
  3022. {
  3023. // Built-in extensions
  3024. return $cfg['system_dir']."/$name/$name.$part.php";
  3025. }
  3026. else
  3027. {
  3028. return $cfg['modules_dir']."/$name/inc/$name.$part.php";
  3029. }
  3030. }
  3031. /**
  3032. * Returns a language file path for an extension or core part.
  3033. *
  3034. * @param string $name Part name (area code or plugin name)
  3035. * @param string $type Part type: 'plug', 'module' or 'core'
  3036. * @param string $default Default (fallback) language code
  3037. * @param string $lang Set this to override global $lang
  3038. * @return mixed Langfile path or FALSE if no suitable files were found
  3039. */
  3040. function cot_langfile($name, $type = 'plug', $default = 'en', $lang = null)
  3041. {
  3042. global $cfg;
  3043. if (!is_string($lang))
  3044. {
  3045. global $lang;
  3046. }
  3047. if ($type == 'module')
  3048. {
  3049. if (@file_exists($cfg['lang_dir']."/$lang/modules/$name.$lang.lang.php"))
  3050. {
  3051. return $cfg['lang_dir']."/$lang/modules/$name.$lang.lang.php";
  3052. }
  3053. elseif (@file_exists($cfg['modules_dir']."/$name/lang/$name.$lang.lang.php"))
  3054. {
  3055. return $cfg['modules_dir']."/$name/lang/$name.$lang.lang.php";
  3056. }
  3057. elseif (@file_exists($cfg['modules_dir']."/$name/lang/$name.$default.lang.php"))
  3058. {
  3059. return $cfg['modules_dir']."/$name/lang/$name.$default.lang.php";
  3060. }
  3061. }
  3062. elseif ($type == 'core')
  3063. {
  3064. if (@file_exists($cfg['lang_dir']."/$lang/$name.$lang.lang.php"))
  3065. {
  3066. return $cfg['lang_dir']."/$lang/$name.$lang.lang.php";
  3067. }
  3068. elseif (@file_exists($cfg['lang_dir']."/$default/$name.$default.lang.php"))
  3069. {
  3070. return $cfg['lang_dir']."/$default/$name.$default.lang.php";
  3071. }
  3072. }
  3073. else
  3074. {
  3075. if (@file_exists($cfg['lang_dir']."/$lang/plugins/$name.$lang.lang.php"))
  3076. {
  3077. return $cfg['lang_dir']."/$lang/plugins/$name.$lang.lang.php";
  3078. }
  3079. elseif (@file_exists($cfg['plugins_dir']."/$name/lang/$name.$lang.lang.php"))
  3080. {
  3081. return $cfg['plugins_dir']."/$name/lang/$name.$lang.lang.php";
  3082. }
  3083. elseif (@file_exists($cfg['plugins_dir']."/$name/lang/$name.$default.lang.php"))
  3084. {
  3085. return $cfg['plugins_dir']."/$name/lang/$name.$default.lang.php";
  3086. }
  3087. }
  3088. return false;
  3089. }
  3090. /**
  3091. * Returns a exists language from HTTP_ACCEPT_LANGUAGE
  3092. *
  3093. * @return string
  3094. */
  3095. function cot_lang_determine()
  3096. {
  3097. global $cfg;
  3098. if (($list = strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE'])))
  3099. {
  3100. if (preg_match_all('/([a-z]{1,8}(?:-[a-z]{1,8})?)(?:;q=([0-9.]+))?/', $list, $list))
  3101. {
  3102. $language = array_combine($list[1], $list[2]);
  3103. //
  3104. foreach ($language as $n => $v)
  3105. {
  3106. $language[$n] = $v ? $v : 1;
  3107. }
  3108. arsort($language, SORT_NUMERIC);
  3109. foreach ($language as $n => $v)
  3110. {
  3111. if (@file_exists($cfg['lang_dir']."/$n/main.$n.lang.php"))
  3112. {
  3113. return $n;
  3114. }
  3115. }
  3116. }
  3117. }
  3118. return 'en';
  3119. }
  3120. /**
  3121. * Removes a directory recursively
  3122. * @param string $dir Directory path
  3123. * @return int Number of files and folders removed
  3124. */
  3125. function cot_rmdir($dir)
  3126. {
  3127. if(empty($dir) && $dir != '0') return false;
  3128. static $cnt = 0;
  3129. if (is_dir($dir)) {
  3130. $objects = scandir($dir);
  3131. foreach ($objects as $f) {
  3132. $path = $dir . DIRECTORY_SEPARATOR . $f;
  3133. if ($f != "." && $f != "..")
  3134. {
  3135. if (filetype($path) == "dir")
  3136. {
  3137. cot_rmdir($path);
  3138. }
  3139. else
  3140. {
  3141. unlink($path);
  3142. $cnt++;
  3143. }
  3144. }
  3145. }
  3146. reset($objects);
  3147. rmdir($dir);
  3148. $cnt++;
  3149. }
  3150. return $cnt;
  3151. }
  3152. /**
  3153. * Returns path to a CSS file for user selected color scheme.
  3154. * The default search order is:
  3155. * 1) `css` subfolder of user selected theme
  3156. * 2) Main folder of user selected theme
  3157. *
  3158. * @return mixed Filename with full path to CSS file or FALSE if not found
  3159. */
  3160. function cot_schemefile()
  3161. {
  3162. // cot class can be not initialised yet
  3163. global $cfg, $usr;
  3164. $scheme = isset($usr['scheme']) ? $usr['scheme'] : $cfg['defaultscheme'];
  3165. $theme = isset($usr['theme']) ? $usr['theme'] : $cfg['defaulttheme'];
  3166. $scheme_css = array();
  3167. $scheme_css[] = $cfg['themes_dir'] .'/'. $theme .'/css/'. $scheme .'.css';
  3168. $scheme_css[] = $cfg['themes_dir'] .'/'. $theme .'/'. $scheme .'.css';
  3169. foreach ($scheme_css as $filename) {
  3170. if (is_file($filename)) return $filename;
  3171. }
  3172. return false;
  3173. }
  3174. /**
  3175. * Returns path to a template file. The default search order is:
  3176. * 1) Current theme folder (plugins/ subdir for plugins, admin/ subdir for admin)
  3177. * 2) Default theme folder (if current is not default)
  3178. * 3) tpl subdir in module/plugin folder (fallback template)
  3179. *
  3180. * @param mixed $base Item name (string), or base names (array)
  3181. * @param string $type Extension type: 'plug', 'module' or 'core'
  3182. * @param bool $admin Use admin theme file if present. Tries to determine from base string by default.
  3183. * @return string
  3184. */
  3185. function cot_tplfile($base, $type = 'module', $admin = null)
  3186. {
  3187. global $usr, $cfg;
  3188. // Get base name parts
  3189. if (is_string($base) && mb_strpos($base, '.') !== false) {
  3190. $base = explode('.', $base);
  3191. }
  3192. if (!is_array($base)) {
  3193. $base = array($base);
  3194. }
  3195. if (is_null($admin)) {
  3196. $admin = ($base[0] == 'admin' || (isset($base[1]) && $base[1] == 'admin'));
  3197. }
  3198. $scan_dirs = array();
  3199. // Possible search directories depending on extension type
  3200. if ($type == 'plug') {
  3201. // Plugin template paths
  3202. $admin && !empty($cfg['admintheme']) && $scan_dirs[] = "{$cfg['themes_dir']}/admin/{$cfg['admintheme']}/plugins/";
  3203. $admin && $scan_dirs[] = "{$cfg['themes_dir']}/{$usr['theme']}/admin/plugins/";
  3204. if (isset(cot::$usr['theme'])) {
  3205. $scan_dirs[] = cot::$cfg['themes_dir']."/{$usr['theme']}/plugins/";
  3206. $scan_dirs[] = cot::$cfg['themes_dir']."/{$usr['theme']}/plugins/{$base[0]}/";
  3207. }
  3208. $scan_dirs[] = "{$cfg['plugins_dir']}/{$base[0]}/tpl/";
  3209. } elseif ($type == 'core' && in_array($base[0], array('admin', 'header', 'footer', 'message'))) {
  3210. // Built-in core modules
  3211. !empty($cfg['admintheme']) && $scan_dirs[] = "{$cfg['themes_dir']}/admin/{$cfg['admintheme']}/";
  3212. if (isset(cot::$usr['theme'])) {
  3213. $scan_dirs[] = "{$cfg['themes_dir']}/{$usr['theme']}/admin/";
  3214. }
  3215. $scan_dirs[] = "{$cfg['system_dir']}/admin/tpl/";
  3216. } else {
  3217. // Module template paths
  3218. $admin && !empty($cfg['admintheme']) && $scan_dirs[] = "{$cfg['themes_dir']}/admin/{$cfg['admintheme']}/modules/";
  3219. $admin && $scan_dirs[] = "{$cfg['themes_dir']}/{$usr['theme']}/admin/modules/";
  3220. if (isset(cot::$usr['theme'])) {
  3221. $scan_dirs[] = "{$cfg['themes_dir']}/{$usr['theme']}/";
  3222. $scan_dirs[] = "{$cfg['themes_dir']}/{$usr['theme']}/modules/";
  3223. $scan_dirs[] = "{$cfg['themes_dir']}/{$usr['theme']}/modules/{$base[0]}/";
  3224. }
  3225. $scan_dirs[] = "{$cfg['modules_dir']}/{$base[0]}/tpl/";
  3226. }
  3227. // Build template file name from base parts glued with dots
  3228. $base_depth = count($base);
  3229. for ($i = $base_depth; $i > 0; $i--)
  3230. {
  3231. $levels = array_slice($base, 0, $i);
  3232. $themefile = implode('.', $levels) . '.tpl';
  3233. // Search in all available directories
  3234. foreach ($scan_dirs as $dir)
  3235. {
  3236. if (file_exists($dir . $themefile))
  3237. {
  3238. return $dir . $themefile;
  3239. }
  3240. }
  3241. }
  3242. return false;
  3243. }
  3244. /*
  3245. * ============================ Date and Time Functions =======================
  3246. */
  3247. /**
  3248. * Localized version of PHP date()
  3249. *
  3250. * @see http://php.net/manual/en/function.date.php
  3251. * @param string $format Date/time format as defined in $Ldt or according to PHP date() format
  3252. * @param int $timestamp Unix timestamp
  3253. * @param bool $usertimezone Offset the date with current user's timezone
  3254. * @return string
  3255. */
  3256. function cot_date($format, $timestamp = null, $usertimezone = true)
  3257. {
  3258. global $lang, $L, $Ldt, $usr, $sys;
  3259. if (is_null($timestamp)) {
  3260. $timestamp = cot::$sys['now'];
  3261. }
  3262. if ($usertimezone) {
  3263. $timestamp += cot::$usr['timezone'] * 3600;
  3264. }
  3265. $datetime = (isset($Ldt[$format])) ? @date($Ldt[$format], $timestamp) : @date($format, $timestamp);
  3266. $search = array(
  3267. 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
  3268. 'Friday', 'Saturday', 'Sunday',
  3269. 'Mon', 'Tue', 'Wed', 'Thu',
  3270. 'Fri', 'Sat', 'Sun',
  3271. 'January', 'February', 'March',
  3272. 'April', 'May', 'June',
  3273. 'July', 'August', 'September',
  3274. 'October', 'November', 'December',
  3275. 'Jan', 'Feb', 'Mar',
  3276. 'Apr', 'May', 'Jun',
  3277. 'Jul', 'Aug', 'Sep',
  3278. 'Oct', 'Nov', 'Dec'
  3279. );
  3280. $replace = array(
  3281. cot::$L['Monday'], cot::$L['Tuesday'], cot::$L['Wednesday'], cot::$L['Thursday'],
  3282. cot::$L['Friday'], cot::$L['Saturday'], cot::$L['Sunday'],
  3283. cot::$L['Monday_s'], cot::$L['Tuesday_s'], cot::$L['Wednesday_s'], cot::$L['Thursday_s'],
  3284. cot::$L['Friday_s'], cot::$L['Saturday_s'], cot::$L['Sunday_s'],
  3285. cot::$L['January'], cot::$L['February'], cot::$L['March'],
  3286. cot::$L['April'], cot::$L['May'], cot::$L['June'],
  3287. cot::$L['July'], cot::$L['August'], cot::$L['September'],
  3288. cot::$L['October'], cot::$L['November'], cot::$L['December'],
  3289. cot::$L['January_s'], cot::$L['February_s'], cot::$L['March_s'],
  3290. cot::$L['April_s'], cot::$L['May_s'], cot::$L['June_s'],
  3291. cot::$L['July_s'], cot::$L['August_s'], cot::$L['September_s'],
  3292. cot::$L['October_s'], cot::$L['November_s'], cot::$L['December_s']
  3293. );
  3294. return ($lang == 'en') ? $datetime : str_replace($search, $replace, $datetime);
  3295. }
  3296. /**
  3297. * Creates UNIX timestamp out of a date
  3298. *
  3299. * @param int $hour Hours
  3300. * @param int $minute Minutes
  3301. * @param int $second Seconds
  3302. * @param int $month Month
  3303. * @param int $date Day of the month
  3304. * @param int $year Year
  3305. * @return int
  3306. */
  3307. function cot_mktime($hour = false, $minute = false, $second = false, $month = false, $date = false, $year = false)
  3308. {
  3309. if ($hour === false) $hour = date ('G');
  3310. if ($minute === false) $minute = date ('i');
  3311. if ($second === false) $second = date ('s');
  3312. if ($month === false) $month = date ('n');
  3313. if ($date === false) $date = date ('j');
  3314. if ($year === false) $year = date ('Y');
  3315. return mktime ((int) $hour, (int) $minute, (int) $second, (int) $month, (int) $date, (int) $year);
  3316. }
  3317. if (!function_exists('strptime'))
  3318. {
  3319. /**
  3320. * strptime() for Windows
  3321. * @author ex/yks toolkit
  3322. * @license MIT
  3323. * @param string $date
  3324. * @param string $format
  3325. * @return boolean
  3326. */
  3327. function strptime($date, $format)
  3328. {
  3329. $masks = array(
  3330. '%d' => '(?P<d>[0-9]{2})',
  3331. '%m' => '(?P<m>[0-9]{2})',
  3332. '%Y' => '(?P<Y>[0-9]{4})',
  3333. '%H' => '(?P<H>[0-9]{2})',
  3334. '%M' => '(?P<M>[0-9]{2})',
  3335. '%S' => '(?P<S>[0-9]{2})'
  3336. );
  3337. $rexep = "#" . strtr(preg_quote($format), $masks) . "#";
  3338. if (!preg_match($rexep, $date, $out))
  3339. return false;
  3340. $ret = array(
  3341. "tm_sec" => (int) $out['S'],
  3342. "tm_min" => (int) $out['M'],
  3343. "tm_hour" => (int) $out['H'],
  3344. "tm_mday" => (int) $out['d'],
  3345. "tm_mon" => $out['m'] ? $out['m'] - 1 : 0,
  3346. "tm_year" => $out['Y'] > 1900 ? $out['Y'] - 1900 : 0,
  3347. );
  3348. return $ret;
  3349. }
  3350. }
  3351. /**
  3352. * Converts date into UNIX timestamp.
  3353. * Like strptime() but using formatting as specified for date().
  3354. *
  3355. * @param string $date
  3356. * Formatted date as a string.
  3357. * @param string $format
  3358. * Format on which to base the conversion.
  3359. * Defaults to MySQL date format.
  3360. * Can also be set to 'auto', in which case
  3361. * it will rely on strtotime for parsing.
  3362. * @return int UNIX timestamp or NULL for 0000-00-00
  3363. */
  3364. function cot_date2stamp($date, $format = null)
  3365. {
  3366. if ($date == '0000-00-00' || mb_strtolower($date) == 'null' || is_null($date)) return null;
  3367. if (!$format)
  3368. {
  3369. preg_match('#(\d{4})-(\d{2})-(\d{2})#', $date, $m);
  3370. return mktime(0, 0, 0, (int) $m[2], (int) $m[3], (int) $m[1]);
  3371. }
  3372. if ($format == 'auto')
  3373. {
  3374. return strtotime($date);
  3375. }
  3376. $format = cot_date2strftime($format);
  3377. $m = strptime($date, $format);
  3378. return mktime(
  3379. (int)$m['tm_hour'], (int)$m['tm_min'], (int)$m['tm_sec'],
  3380. (int)$m['tm_mon']+1, (int)$m['tm_mday'], (int)$m['tm_year']+1900
  3381. );
  3382. }
  3383. /**
  3384. * Converts UNIX timestamp into MySQL date
  3385. *
  3386. * @param int $stamp UNIX timestamp
  3387. * @return string MySQL date
  3388. */
  3389. function cot_stamp2date($stamp)
  3390. {
  3391. return date('Y-m-d', $stamp);
  3392. }
  3393. /**
  3394. * Convert a date format to a strftime format.
  3395. * Timezone conversion is done for unix. Windows users must exchange %z and %Z.
  3396. * Unsupported date formats : S, n, t, L, B, G, u, e, I, P, Z, c, r
  3397. * Unsupported strftime formats : %U, %W, %C, %g, %r, %R, %T, %X, %c, %D, %F, %x
  3398. *
  3399. * @see http://php.net/manual/en/function.strftime.php
  3400. * @param string $format A format for date().
  3401. * @return string Format usable for strftime().
  3402. */
  3403. function cot_date2strftime($format) {
  3404. $chars = array(
  3405. 'd' => '%d', 'D' => '%a', 'j' => '%e', 'l' => '%A',
  3406. 'N' => '%u', 'w' => '%w', 'z' => '%j', 'W' => '%V',
  3407. 'F' => '%B', 'm' => '%m', 'M' => '%b', 'o' => '%G',
  3408. 'Y' => '%Y', 'y' => '%y', 'a' => '%P', 'A' => '%p',
  3409. 'g' => '%l', 'h' => '%I', 'H' => '%H', 'i' => '%M',
  3410. 's' => '%S', 'O' => '%z', 'T' => '%Z', 'U' => '%s'
  3411. );
  3412. return strtr((string)$format, $chars);
  3413. }
  3414. /**
  3415. * Returns a list of timezones sorted by GMT offset.
  3416. *
  3417. * @param bool $withgmt Return 'GMT' as the first option, otherwise it won't be included
  3418. * @param bool $dst Include DST in timezone offsets, if DST is in effect there right now
  3419. * @return array Multidimensional array. Each timezone has the following keys:
  3420. * 'identifier' - PHP timezone name, e.g. "America/El_Salvador"
  3421. * 'offset' - GMT offset in seconds, e.g. -21600
  3422. * 'title' - Localized timezone name, e.g. "America/El Salvador"
  3423. * 'description' - Hourly GMT offset and localized name, e.g. "GMT-06:00 America/El Salvador"
  3424. */
  3425. function cot_timezone_list($withgmt = false, $dst = false)
  3426. {
  3427. global $Ltz;
  3428. if (!$Ltz) include cot_langfile('countries', 'core');
  3429. static $timezones = array();
  3430. if (!$timezones) {
  3431. $timezonelist = array();
  3432. $regions = array('Africa', 'America', 'Antarctica', 'Asia', 'Atlantic', 'Europe', 'Indian', 'Pacific');
  3433. $identifiers = DateTimeZone::listIdentifiers();
  3434. foreach ($identifiers as $timezone) {
  3435. $tmp = explode('/', $timezone, 2);
  3436. $region = $tmp[0];
  3437. $city = isset($tmp[1]) ? $tmp[1] : '';
  3438. if (!in_array($region, $regions)) continue;
  3439. $offset = cot_timezone_offset($timezone, false, $dst);
  3440. $gmtoffset = cot_build_timezone($offset);
  3441. $title = isset($Ltz[$timezone]) ? $Ltz[$timezone] : $region.'/'.str_replace('_', ' ', $city);
  3442. $timezonelist[] = array(
  3443. 'identifier' => $timezone,
  3444. 'offset' => $offset,
  3445. 'title' => $title,
  3446. 'description' => "$gmtoffset $title"
  3447. );
  3448. }
  3449. foreach ($timezonelist as $k => $tz) {
  3450. $offsets[$k] = $tz['offset'];
  3451. $names[$k] = $tz['title'];
  3452. }
  3453. array_multisort($offsets, SORT_ASC, $names, SORT_ASC, $timezonelist);
  3454. $timezones = $timezonelist;
  3455. }
  3456. return $withgmt ? array_merge(array(array('name' => 'UTC', 'identifier' => 'UTC', 'offset' => 0, 'description' => 'UTC')), $timezones) : $timezones;
  3457. }
  3458. /**
  3459. * Returns the offset from GMT in seconds or hours, with or without DST.
  3460. * Example: Europe/Amsterdam returns 3600 (GMT+1) in the winter, but 7200 (GMT+2) in the summer (DST).
  3461. * Whether or not to apply DST is determined automatically by PHP, but can be disabled.
  3462. * A list of supported timezone identifiers is here: http://php.net/manual/en/timezones.php
  3463. *
  3464. * @param string $tz Timezone identifier (e.g. Europe/Amsterdam)
  3465. * @param bool $hours Return hours instead of seconds
  3466. * @param bool $dst Include DST in offset if DST is in effect right now
  3467. * @return mixed Timezone difference in seconds (int) or hours (float)
  3468. */
  3469. function cot_timezone_offset($tz, $hours = false, $dst = true)
  3470. {
  3471. if (!$tz || in_array($tz, array('UTC', 'GMT', 'Universal', 'UCT', 'Zulu'))) return 0;
  3472. try
  3473. {
  3474. // $origin_dtz = new DateTimeZone('UTC');
  3475. $remote_dtz = new DateTimeZone($tz);
  3476. if (!$dst)
  3477. {
  3478. // Standard offset is in Winter
  3479. $standard_offset = $remote_dtz->getOffset(new DateTime("next year January 1"));
  3480. // $trans = cot_timezone_transitions($tz);
  3481. // $dstoffset = ($trans['current']['isdst']) ? $trans['current']['offset'] - $trans['previous']['offset'] : 0;
  3482. }
  3483. // $origin_dt = new DateTime('now', $origin_dtz);
  3484. $remote_dt = new DateTime('now', $remote_dtz);
  3485. }
  3486. catch(Exception $e)
  3487. {
  3488. return null;
  3489. }
  3490. // $offset = $remote_dtz->getOffset($remote_dt) - $origin_dtz->getOffset($origin_dt) - $dstoffset;
  3491. $offset = $dst ? $remote_dtz->getOffset($remote_dt) : $standard_offset;
  3492. return $hours ? floatval($offset / 3600) : $offset;
  3493. }
  3494. /**
  3495. * Returns a list of possible timezones based on country and/or GMT offset.
  3496. *
  3497. * @param string $countrycode 2 char lowercase country code
  3498. * @param int $gmtoffset Offset from GMT in seconds
  3499. * @return array Numeric array of timezone identifiers
  3500. */
  3501. function cot_timezone_search($countrycode = '', $gmtoffset = null)
  3502. {
  3503. global $cot_timezones;
  3504. $res = array();
  3505. $both = ($countrycode && is_int($gmtoffset));
  3506. foreach ($cot_timezones as $tz => $info)
  3507. {
  3508. $countrymatch = ($info[0] == $countrycode);
  3509. $offsetmatch = (is_int($gmtoffset) && ($info[1] == $gmtoffset || $info[2] == $gmtoffset));
  3510. if (($countrymatch && $offsetmatch) || (!$both && ($countrymatch || $offsetmatch)))
  3511. {
  3512. $res[] = $tz;
  3513. }
  3514. }
  3515. sort($res);
  3516. return $res;
  3517. }
  3518. /**
  3519. * Returns previous, current and next transition in a certain timezone.
  3520. * Useful for detecting if DST is currently in effect.
  3521. *
  3522. * @param string $tz Timezone identifier, must be one of PHP supported timezones
  3523. * @return array Multidimensional array with keys 'previous', 'current' and 'next',
  3524. * each containing an element of the result of DateTimeZone::getTransitions
  3525. * @see http://www.php.net/manual/en/datetimezone.gettransitions.php
  3526. */
  3527. function cot_timezone_transitions($tz)
  3528. {
  3529. global $sys;
  3530. try
  3531. {
  3532. $dtz = new DateTimeZone($tz);
  3533. }
  3534. catch(Exception $e)
  3535. {
  3536. return null;
  3537. }
  3538. $transitions = array_reverse((array)$dtz->getTransitions());
  3539. foreach ($transitions as $key => $transition)
  3540. {
  3541. if ($transition['ts'] < $sys['now'])
  3542. {
  3543. return array(
  3544. 'previous' => $transitions[$key+1],
  3545. 'current' => $transition,
  3546. 'next' => $transitions[$key-1],
  3547. );
  3548. }
  3549. }
  3550. }
  3551. /*
  3552. * ================================== Pagination ==============================
  3553. */
  3554. /**
  3555. * Page navigation (pagination) builder. Uses URL transformation and resource strings,
  3556. * returns an associative array, containing:
  3557. * ['prev'] - first and previous page buttons
  3558. * ['main'] - buttons with page numbers, including current
  3559. * ['next'] - next and last page buttons
  3560. * ['last'] - last page with number
  3561. *
  3562. * @param string $module Site area or script name
  3563. * @param mixed $params URL parameters as array or parameter string
  3564. * @param int $current Current page offset
  3565. * @param int $entries Total rows
  3566. * @param int $perpage Rows per page
  3567. * @param string $characters It is symbol for parametre which transfer pagination
  3568. * @param string $hash Hash part of the url (including #)
  3569. * @param bool $ajax Add AJAX support
  3570. * @param string $target_div Target div ID if $ajax is true
  3571. * @param string $ajax_module Site area name for ajax if different from $module
  3572. * @param string $ajax_params URL parameters for ajax if $ajax_module is not empty
  3573. * @return array
  3574. */
  3575. function cot_pagenav($module, $params, $current, $entries, $perpage, $characters = 'd', $hash = '',
  3576. $ajax = false, $target_div = '', $ajax_module = '', $ajax_params = array())
  3577. {
  3578. if (function_exists('cot_pagenav_custom')) {
  3579. // For custom pagination functions in plugins
  3580. return cot_pagenav_custom($module, $params, $current, $entries, $perpage, $characters, $hash,
  3581. $ajax, $target_div, $ajax_module, $ajax_params);
  3582. }
  3583. if (!$perpage) {
  3584. $perpage = cot::$cfg['maxrowsperpage'] ? cot::$cfg['maxrowsperpage'] : 1;
  3585. }
  3586. $onpage = $entries - $current;
  3587. if ($onpage > $perpage) $onpage = $perpage;
  3588. if ($entries <= $perpage) {
  3589. return array(
  3590. 'prev' => null,
  3591. 'main' => null,
  3592. 'next' => null,
  3593. 'last' => null,
  3594. 'current' => 1,
  3595. 'firstlink' => null,
  3596. 'prevlink' => null,
  3597. 'nextlink' => null,
  3598. 'lastlink' => null,
  3599. 'total' => 1,
  3600. 'onpage' => $onpage,
  3601. 'entries' => $entries
  3602. );
  3603. }
  3604. $each_side = 3; // Links each side
  3605. $base_rel = '';
  3606. is_array($params) ? $args = $params : parse_str($params, $args);
  3607. if ($ajax) {
  3608. $ajax_rel = !empty($ajax_module);
  3609. $ajax_rel && is_string($ajax_params) ? parse_str($ajax_params, $ajax_args) : $ajax_args = $ajax_params;
  3610. $event = ' class="ajax"';
  3611. if (empty($target_div)) {
  3612. $base_rel = $ajax_rel ? ' rel="get;' : '';
  3613. } else {
  3614. $base_rel = $ajax_rel ? ' rel="get-'.$target_div.';' : ' rel="get-'.$target_div.'"';
  3615. }
  3616. } else {
  3617. $ajax_rel = false;
  3618. $event = '';
  3619. }
  3620. $rel = '';
  3621. $totalpages = ceil($entries / $perpage);
  3622. $currentpage = floor($current / $perpage) + 1;
  3623. $cur_left = $currentpage - $each_side;
  3624. if ($cur_left < 1) $cur_left = 1;
  3625. $cur_right = $currentpage + $each_side;
  3626. if ($cur_right > $totalpages) $cur_right = $totalpages;
  3627. // Main block
  3628. $first = '';
  3629. $firstlink = '';
  3630. $prev = '';
  3631. $prevlink = '';
  3632. $next = '';
  3633. $nextlink = '';
  3634. $last = '';
  3635. $lastlink = '';
  3636. $lastn = FALSE;
  3637. $before = '';
  3638. $pages = '';
  3639. $after = '';
  3640. $i = 1;
  3641. $n = 0;
  3642. while ($i < $cur_left) {
  3643. if (cot::$cfg['easypagenav']) {
  3644. $args[$characters] = $i == 1 ? null : $i;
  3645. } else {
  3646. $args[$characters] = ($i - 1) * $perpage;
  3647. }
  3648. if ($ajax_rel) {
  3649. $ajax_args[$characters] = $args[$characters];
  3650. $rel = $base_rel.str_replace('?', ';', cot_url($ajax_module, $ajax_args)).'"';
  3651. } else {
  3652. $rel = $base_rel;
  3653. }
  3654. $before .= cot_rc('link_pagenav_main', array(
  3655. 'url' => cot_url($module, $args, $hash),
  3656. 'event' => $event,
  3657. 'rel' => $rel,
  3658. 'num' => $i
  3659. ));
  3660. if ($i < $cur_left - 2) {
  3661. $before .= cot::$R['link_pagenav_gap'];
  3662. } elseif ($i == $cur_left - 2) {
  3663. if (cot::$cfg['easypagenav']) {
  3664. $args[$characters] = $i+1;
  3665. } else {
  3666. $args[$characters] = $i * $perpage;
  3667. }
  3668. if ($ajax_rel) {
  3669. $ajax_args[$characters] = $args[$characters];
  3670. $rel = $base_rel.str_replace('?', ';', cot_url($ajax_module, $ajax_args)).'"';
  3671. } else {
  3672. $rel = $base_rel;
  3673. }
  3674. $before .= cot_rc('link_pagenav_main', array(
  3675. 'url' => cot_url($module, $args, $hash),
  3676. 'event' => $event,
  3677. 'rel' => $rel,
  3678. 'num' => $i + 1
  3679. ));
  3680. }
  3681. $i *= ($n % 2) ? 2 : 5;
  3682. $n++;
  3683. }
  3684. for ($j = $cur_left; $j <= $cur_right; $j++)
  3685. {
  3686. if (cot::$cfg['easypagenav'])
  3687. {
  3688. $args[$characters] = $j == 1 ? null : $j;
  3689. }
  3690. else
  3691. {
  3692. $args[$characters] = ($j - 1) * $perpage;
  3693. }
  3694. if ($ajax_rel)
  3695. {
  3696. $ajax_args[$characters] = $args[$characters];
  3697. $rel = $base_rel.str_replace('?', ';', cot_url($ajax_module, $ajax_args)).'"';
  3698. }
  3699. else
  3700. {
  3701. $rel = $base_rel;
  3702. }
  3703. $rc = $j == $currentpage ? 'current' : 'main';
  3704. $pages .= cot_rc('link_pagenav_'.$rc, array(
  3705. 'url' => cot_url($module, $args, $hash),
  3706. 'event' => $event,
  3707. 'rel' => $rel,
  3708. 'num' => $j
  3709. ));
  3710. }
  3711. while ($i <= $cur_right)
  3712. {
  3713. $i *= ($n % 2) ? 2 : 5;
  3714. $n++;
  3715. }
  3716. while ($i < $totalpages)
  3717. {
  3718. if ($i > $cur_right + 2)
  3719. {
  3720. $after .= cot::$R['link_pagenav_gap'];
  3721. }
  3722. elseif ($i == $cur_right + 2)
  3723. {
  3724. if (cot::$cfg['easypagenav'])
  3725. {
  3726. $args[$characters] = $i == 2 ? null : $i - 1;
  3727. }
  3728. else
  3729. {
  3730. $args[$characters] = ($i - 2) * $perpage;
  3731. }
  3732. if ($ajax_rel)
  3733. {
  3734. $ajax_args[$characters] = $args[$characters];
  3735. $rel = $base_rel.str_replace('?', ';', cot_url($ajax_module, $ajax_args)).'"';
  3736. }
  3737. else
  3738. {
  3739. $rel = $base_rel;
  3740. }
  3741. $after .= cot_rc('link_pagenav_main', array(
  3742. 'url' => cot_url($module, $args, $hash),
  3743. 'event' => $event,
  3744. 'rel' => $rel,
  3745. 'num' => $i - 1
  3746. ));
  3747. }
  3748. if (cot::$cfg['easypagenav'])
  3749. {
  3750. $args[$characters] = $i == 1 ? null : $i;
  3751. }
  3752. else
  3753. {
  3754. $args[$characters] = ($i - 1) * $perpage;
  3755. }
  3756. if ($ajax_rel)
  3757. {
  3758. $ajax_args[$characters] = $args[$characters];
  3759. $rel = $base_rel.str_replace('?', ';', cot_url($ajax_module, $ajax_args)).'"';
  3760. }
  3761. else
  3762. {
  3763. $rel = $base_rel;
  3764. }
  3765. $after .= cot_rc('link_pagenav_main', array(
  3766. 'url' => cot_url($module, $args, $hash),
  3767. 'event' => $event,
  3768. 'rel' => $rel,
  3769. 'num' => $i
  3770. ));
  3771. $i *= ($n % 2) ? 2 : 5;
  3772. $n++;
  3773. }
  3774. $pages = $before.$pages.$after;
  3775. // Previous/next
  3776. if ($current > 0)
  3777. {
  3778. $prev_n = $current - $perpage;
  3779. if ($prev_n < 0)
  3780. {
  3781. $prev_n = 0;
  3782. }
  3783. if (cot::$cfg['easypagenav'])
  3784. {
  3785. $num = floor($prev_n / $perpage) + 1;
  3786. $args[$characters] = $num == 1 ? null : $num;
  3787. }
  3788. else
  3789. {
  3790. $args[$characters] = $prev_n;
  3791. }
  3792. if ($ajax_rel)
  3793. {
  3794. $ajax_args[$characters] = $args[$characters];
  3795. $rel = $base_rel.str_replace('?', ';', cot_url($ajax_module, $ajax_args)).'"';
  3796. }
  3797. else
  3798. {
  3799. $rel = $base_rel;
  3800. }
  3801. $prevlink = cot_url($module, $args, $hash);
  3802. $prev = cot_rc('link_pagenav_prev', array(
  3803. 'url' => $prevlink,
  3804. 'event' => $event,
  3805. 'rel' => $rel,
  3806. 'num' => $prev_n + 1
  3807. ));
  3808. $args[$characters] = 0;
  3809. if ($ajax_rel)
  3810. {
  3811. $ajax_args[$characters] = $args[$characters];
  3812. $rel = $base_rel.str_replace('?', ';', cot_url($ajax_module, $ajax_args)).'"';
  3813. }
  3814. else
  3815. {
  3816. $rel = $base_rel;
  3817. }
  3818. unset($args[$characters]);
  3819. $firstlink = cot_url($module, $args, $hash);
  3820. $first = cot_rc('link_pagenav_first', array(
  3821. 'url' => $firstlink,
  3822. 'event' => $event,
  3823. 'rel' => $rel,
  3824. 'num' => 1
  3825. ));
  3826. }
  3827. if (($current + $perpage) < $entries) {
  3828. $next_n = $current + $perpage;
  3829. if (cot::$cfg['easypagenav']) {
  3830. $num = floor($next_n / $perpage) + 1;
  3831. $args[$characters] = $num == 1 ? null : $num;
  3832. } else {
  3833. $args[$characters] = $next_n;
  3834. }
  3835. if ($ajax_rel) {
  3836. $ajax_args[$characters] = $args[$characters];
  3837. $rel = $base_rel.str_replace('?', ';', cot_url($ajax_module, $ajax_args)).'"';
  3838. } else {
  3839. $rel = $base_rel;
  3840. }
  3841. $nextlink = cot_url($module, $args, $hash);
  3842. $next = cot_rc('link_pagenav_next', array(
  3843. 'url' => $nextlink,
  3844. 'event' => $event,
  3845. 'rel' => $rel,
  3846. 'num' => $next_n + 1
  3847. ));
  3848. $last_n = ($totalpages - 1) * $perpage;
  3849. if (cot::$cfg['easypagenav']) {
  3850. $num = floor($last_n / $perpage) + 1;
  3851. $args[$characters] = $num == 1 ? null : $num;
  3852. } else {
  3853. $args[$characters] = $last_n;
  3854. }
  3855. if ($ajax_rel) {
  3856. $ajax_args[$characters] = $args[$characters];
  3857. $rel = $base_rel.str_replace('?', ';', cot_url($ajax_module, $ajax_args)).'"';
  3858. } else {
  3859. $rel = $base_rel;
  3860. }
  3861. $lastlink = cot_url($module, $args, $hash);
  3862. $last = cot_rc('link_pagenav_last', array(
  3863. 'url' => $lastlink,
  3864. 'event' => $event,
  3865. 'rel' => $rel,
  3866. 'num' => $last_n + 1
  3867. ));
  3868. $lastn = (($last_n + $perpage) < $entries) ?
  3869. cot_rc('link_pagenav_main', array(
  3870. 'url' => cot_url($module, $args, $hash),
  3871. 'event' => $event,
  3872. 'rel' => $rel,
  3873. 'num' => floor($last_n / $perpage) + 1
  3874. )): FALSE;
  3875. }
  3876. return array(
  3877. 'prev' => $first.$prev,
  3878. 'main' => $pages,
  3879. 'next' => $next.$last,
  3880. 'last' => $lastn,
  3881. 'current' => $currentpage,
  3882. 'firstlink' => $firstlink,
  3883. 'prevlink' => $prevlink,
  3884. 'nextlink' => $nextlink,
  3885. 'lastlink' => $lastlink,
  3886. 'total' => $totalpages,
  3887. 'onpage' => $onpage,
  3888. 'entries' => $entries
  3889. );
  3890. }
  3891. /*
  3892. * ============================== Text parsing API ============================
  3893. */
  3894. /**
  3895. * Returns the list of available rich text editors
  3896. *
  3897. * @return array
  3898. */
  3899. function cot_get_editors()
  3900. {
  3901. global $cot_plugins;
  3902. $list = array('none');
  3903. if (is_array($cot_plugins['editor']))
  3904. {
  3905. foreach ($cot_plugins['editor'] as $k)
  3906. {
  3907. if (cot_auth('plug', $k['pl_code'], 'W'))
  3908. {
  3909. $list[] = $k['pl_code'];
  3910. }
  3911. }
  3912. }
  3913. return $list;
  3914. }
  3915. /**
  3916. * Returns the list of available markup parsers
  3917. *
  3918. * @return array
  3919. */
  3920. function cot_get_parsers()
  3921. {
  3922. global $cot_plugins;
  3923. $list = array('none');
  3924. if (is_array($cot_plugins['parser']))
  3925. {
  3926. foreach ($cot_plugins['parser'] as $k)
  3927. {
  3928. if (cot_auth('plug', $k['pl_code'], 'W'))
  3929. {
  3930. $list[] = $k['pl_code'];
  3931. }
  3932. }
  3933. }
  3934. return $list;
  3935. }
  3936. /**
  3937. * Parses text body
  3938. *
  3939. * @param string $text Source text
  3940. * @param bool $enable_markup Enable markup or plain text output
  3941. * @param string $parser Non-default parser to use
  3942. * @return string
  3943. */
  3944. function cot_parse($text, $enable_markup = true, $parser = '')
  3945. {
  3946. global $cfg, $cot_plugins;
  3947. $plain = true;
  3948. if ($enable_markup)
  3949. {
  3950. if (empty($parser))
  3951. {
  3952. $parser = $cfg['parser'];
  3953. }
  3954. if (!empty($parser) && $parser != 'none')
  3955. {
  3956. $func = "cot_parse_$parser";
  3957. if (function_exists($func))
  3958. {
  3959. $text = $func($text);
  3960. $plain = false;
  3961. }
  3962. else
  3963. {
  3964. // Load the appropriate parser
  3965. if (is_array($cot_plugins['parser']))
  3966. {
  3967. foreach ($cot_plugins['parser'] as $k)
  3968. {
  3969. if ($k['pl_code'] == $parser && cot_auth('plug', $k['pl_code'], 'R'))
  3970. {
  3971. include $cfg['plugins_dir'] . '/' . $k['pl_file'];
  3972. $text = $func($text);
  3973. $plain = false;
  3974. break;
  3975. }
  3976. }
  3977. }
  3978. }
  3979. }
  3980. }
  3981. if ($plain)
  3982. {
  3983. $text = nl2br(htmlspecialchars($text));
  3984. }
  3985. /* == Hook == */
  3986. foreach (cot_getextplugins('parser.last') as $pl)
  3987. {
  3988. include $pl;
  3989. }
  3990. /* ===== */
  3991. return $text;
  3992. }
  3993. /**
  3994. * Automatically detect and parse URLs in text into HTML
  3995. *
  3996. * @param string $text Text body
  3997. * @return string
  3998. */
  3999. function cot_parse_autourls($text)
  4000. {
  4001. $text = preg_replace('`(^|\s)(http|https|ftp)://([^\s"\'\[]+)`', '$1<a href="$2://$3">$2://$3</a>', $text);
  4002. return $text;
  4003. }
  4004. /**
  4005. * Truncates text.
  4006. *
  4007. * Cuts a string to the length of $length
  4008. *
  4009. * @param string $text String to truncate.
  4010. * @param integer $length Length of returned string, including ellipsis.
  4011. * @param boolean $considerhtml If true, HTML tags would be handled correctly *
  4012. * @param boolean $exact If false, $text will not be cut mid-word
  4013. * @param string $cuttext Adds text if truncated
  4014. * @return string trimmed string.
  4015. */
  4016. function cot_string_truncate($text, $length = 100, $considerhtml = true, $exact = false, $cuttext = '')
  4017. {
  4018. $truncated_by_space = false;
  4019. if ($considerhtml) {
  4020. // if the plain text is shorter than the maximum length, return the whole text
  4021. if (!preg_match('/<\s*(pre|plaintext)/', $text) && mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) {
  4022. return $text;
  4023. }
  4024. // splits all html-tags to scanable lines
  4025. preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER);
  4026. $total_length = 0;
  4027. $open_tags = array();
  4028. $truncate = '';
  4029. $plain_mode = false;
  4030. $plain_tag = false;
  4031. foreach ($lines as $line_matchings) {
  4032. // if there is any html-tag in this line, handle it and add it (uncounted) to the output
  4033. if (!empty($line_matchings[1])) {
  4034. // if it's an "empty element" with or without xhtml-conform closing slash (f.e. <br/>)
  4035. if (preg_match('/^<(\s*.+?\/\s*|\s*(img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param)(\s.+?)?)>$/is', $line_matchings[1]))
  4036. {
  4037. // do nothing
  4038. }
  4039. // if tag is a closing tag (f.e. </b>)
  4040. elseif (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings))
  4041. {
  4042. $tag = false;
  4043. if (strtolower($tag_matchings[1]) == $plain_mode) {
  4044. $plain_mode = false;
  4045. } else {
  4046. // delete tag from $open_tags list
  4047. $pos = array_search($tag_matchings[1], $open_tags);
  4048. if ($pos !== false) {
  4049. unset($open_tags[$pos]);
  4050. }
  4051. }
  4052. }
  4053. // if tag is an opening tag (f.e. <b>)
  4054. elseif (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings))
  4055. {
  4056. $tag = strtolower($tag_matchings[1]);
  4057. $plain_tag = in_array($tag, array('pre','plaintext')) ? $tag : false;
  4058. // add tag to the beginning of $open_tags list
  4059. if (!$plain_mode && !$plain_tag) array_unshift($open_tags, mb_strtolower($tag));
  4060. }
  4061. // add html-tag to $truncate'd text
  4062. if (!$plain_mode) $truncate .= $line_matchings[1];
  4063. }
  4064. // the number of characters which are left
  4065. $left = $length - $total_length;
  4066. if ($plain_mode || ($plain_tag && $tag)) {
  4067. // treats text as plain in <pre>, <plaintext> tags
  4068. $content = $plain_mode ? $line_matchings[0] : $line_matchings[2];
  4069. if (mb_strlen($content) <= $left) {
  4070. $truncate .= $content;
  4071. $total_length += mb_strlen($content);
  4072. } else {
  4073. $truncate .= mb_substr($content, 0, $left);
  4074. $total_length += $left;
  4075. }
  4076. if ($plain_tag && !$plain_mode) $plain_mode = $plain_tag;
  4077. } else {
  4078. // calculate the length of the plain text part of the line; handle entities as one character
  4079. $content_length = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};|[\r\n\s]{2,}/i', ' ', $line_matchings[2]));
  4080. if ($total_length+$content_length> $length)
  4081. {
  4082. $entities_length = 0;
  4083. // search for html entities and spaces
  4084. if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};|[\r\n\s]{2,}/i', $line_matchings[2], $entities, PREG_OFFSET_CAPTURE))
  4085. {
  4086. // calculate the real length of all entities in the legal range
  4087. foreach ($entities[0] as $entity)
  4088. {
  4089. if ($entity[1]+1-$entities_length <= $left)
  4090. {
  4091. $left--;
  4092. $entities_length += mb_strlen($entity[0]);
  4093. }
  4094. else
  4095. {
  4096. // no more characters left
  4097. break;
  4098. }
  4099. }
  4100. }
  4101. $truncate .= mb_substr($line_matchings[2], 0, $left+$entities_length);
  4102. // maximum lenght is reached, so get off the loop
  4103. $truncated_by_space = preg_match('/[\r\n\s]/', mb_substr($line_matchings[2], $left+$entities_length, 1));
  4104. break;
  4105. }
  4106. else
  4107. {
  4108. $truncate .= $line_matchings[2];
  4109. $total_length += $content_length;
  4110. }
  4111. }
  4112. // if the maximum length is reached, get off the loop
  4113. if ($total_length >= $length) {
  4114. break;
  4115. }
  4116. }
  4117. } else {
  4118. if (mb_strlen($text) <= $length) {
  4119. return $text;
  4120. } else {
  4121. $truncate = mb_substr($text, 0, $length);
  4122. }
  4123. }
  4124. if (!$exact && !$truncated_by_space && !$plain_mode) {
  4125. // ...search the last occurence of a space...
  4126. if (mb_strrpos($truncate, ' ') > 0) {
  4127. $pos1 = mb_strrpos($truncate, ' ');
  4128. $pos2 = mb_strrpos($truncate, '>');
  4129. $spos = ($pos2 < $pos1) ? $pos1 : ($pos2+1);
  4130. if (isset($spos)) {
  4131. // ...and cut the text in this position
  4132. $truncate = mb_substr($truncate, 0, $spos);
  4133. }
  4134. }
  4135. }
  4136. $truncate .= $cuttext;
  4137. if ($considerhtml) {
  4138. // close all unclosed html-tags
  4139. if ($plain_mode) $truncate .= '</'.$plain_mode.'>';
  4140. foreach ($open_tags as $tag) {
  4141. $truncate .= '</'.$tag.'>';
  4142. }
  4143. }
  4144. return $truncate;
  4145. }
  4146. /**
  4147. * Wraps text
  4148. *
  4149. * @param string $str Source text
  4150. * @param int $wrap Wrapping boundary
  4151. * @return string
  4152. */
  4153. function cot_wraptext($str, $wrap = 80)
  4154. {
  4155. if (!empty($str)) {
  4156. $str = preg_replace('/([^\n\r ?&\.\/<>\"\\-]{'.$wrap.'})/', " \\1\n", $str);
  4157. }
  4158. return $str;
  4159. }
  4160. /*
  4161. * ============================== Resource Strings ============================
  4162. */
  4163. /**
  4164. * Resource string formatter function. Takes a string with predefined variable substitution, e.g.
  4165. * 'My {$pet} likes {$food}. And {$pet} is hungry!' and an assotiative array of substitution values, e.g.
  4166. * array('pet' => 'rabbit', 'food' => 'carrots') and assembles a formatted result. If {$var} cannot be found
  4167. * in $args, it will be taken from global scope. You can also use parameter strings instead of arrays, e.g.
  4168. * 'pet=rabbit&food=carrots'. Or omit the second parameter in case all substitutions are globals.
  4169. *
  4170. * @global array $R Resource strings
  4171. * @global array $L Language strings, support resource sequences too
  4172. * @param string $name Name of the $R item or a resource string itself
  4173. * @param array $params Associative array of arguments or a parameter string
  4174. * @return string Assembled resource string
  4175. */
  4176. function cot_rc($name, $params = array())
  4177. {
  4178. global $R, $L, $theme_reload;
  4179. if (isset($R[$name]) && is_array($theme_reload) && !empty($theme_reload['R'][$name])) {
  4180. $R[$name] = $theme_reload['R'][$name];
  4181. } elseif (isset($L[$name]) && is_array($theme_reload) && !empty($theme_reload['L'][$name])) {
  4182. $L[$name] = $theme_reload['L'][$name];
  4183. }
  4184. if(isset($R[$name])) {
  4185. $res = $R[$name];
  4186. } elseif(isset($L[$name])) {
  4187. $res = $L[$name];
  4188. } else {
  4189. $res = $name;
  4190. }
  4191. is_array($params) ? $args = $params : parse_str($params, $args);
  4192. if (preg_match_all('#\{\$(\w+)\}#', $res, $matches, PREG_SET_ORDER)) {
  4193. foreach($matches as $m) {
  4194. $var = $m[1];
  4195. $val = null;
  4196. if(isset($args[$var])) {
  4197. $val = $args[$var];
  4198. } elseif(isset($GLOBALS[$var])) {
  4199. $val = $GLOBALS[$var];
  4200. }
  4201. if($val !== null) {
  4202. $res = str_replace($m[0], $val, $res);
  4203. }
  4204. }
  4205. }
  4206. return $res;
  4207. }
  4208. /**
  4209. * Converts custom attributes to a string if necessary
  4210. *
  4211. * @param mixed $attrs A string or associative array
  4212. * @return string
  4213. */
  4214. function cot_rc_attr_string($attrs)
  4215. {
  4216. $attr_str = '';
  4217. if (is_array($attrs))
  4218. {
  4219. foreach ($attrs as $key => $val)
  4220. {
  4221. $attr_str .= ' ' . $key . '="' . htmlspecialchars($val) . '"';
  4222. }
  4223. }
  4224. elseif ($attrs)
  4225. {
  4226. $attr_str = ' ' . $attrs;
  4227. }
  4228. return $attr_str;
  4229. }
  4230. /**
  4231. * Modifies rc string
  4232. *
  4233. * @param string $rc A resource string
  4234. * @param mixed $attrs A string or associative array
  4235. * @return string
  4236. */
  4237. function cot_rc_modify($rc, $attrs)
  4238. {
  4239. if (!is_array($attrs))
  4240. {
  4241. preg_match_all("/(([a-z0-9-_]+)=(\"|')(.*?)(\"|'))/", $attrs, $matches);
  4242. $attrs = array();
  4243. foreach ($matches[2] as $key => $value)
  4244. {
  4245. $attrs[$value] = $matches[4][$key];
  4246. }
  4247. }
  4248. foreach ($attrs as $key => $value)
  4249. {
  4250. if(mb_stripos($rc, " ".$key."=") !== false)
  4251. {
  4252. $rc = preg_replace("/".$key."=(\"|')(.*?)(\"|')/", $key.'="'.$value.'"', $rc);
  4253. }
  4254. else
  4255. {
  4256. $rc = preg_replace("/<([^\/ ]+)(.+)/", "<$1 ".$key.'="'.$value.'"$2', $rc);
  4257. }
  4258. }
  4259. return($rc);
  4260. }
  4261. /**
  4262. * Puts a portion of embedded code into the header/footer CSS/JS resource registry.
  4263. *
  4264. * It is strongly recommended to use files for CSS/JS whenever possible
  4265. * and call cot_rc_add_file() function for them instead of embedding code
  4266. * into the page and using this function. This function should be used for
  4267. * dynamically generated code, which cannot be stored in static files.
  4268. *
  4269. * @global array $cot_rc_reg_css Header CSS resource registry
  4270. * @param string $identifier Alphanumeric identifier for the piece, used to control updates, etc.
  4271. * @param string $code Embedded stylesheet or script code
  4272. * @param string $scope Resource scope. See description of this parameter in cot_rc_add_file() docs.
  4273. * @param string $type Resource type: 'js' or 'css'
  4274. * @param int $order Order priority number
  4275. * @return bool This function always returns TRUE
  4276. * @see cot_rc_add_file()
  4277. * @global Cache $cache
  4278. *
  4279. * @deprecated Will be removed in v.1.0. Use Resources::addEmbed() instead
  4280. */
  4281. function cot_rc_add_embed($identifier, $code, $scope = 'global', $type = 'js', $order = 50)
  4282. {
  4283. return Resources::addEmbed($code, $type, $order, $scope, $identifier);
  4284. }
  4285. /**
  4286. * Puts a JS/CSS file into the footer resource registry to be consolidated with other
  4287. * such resources and stored in cache.
  4288. *
  4289. * It is recommened to use files instead of embedded code and use this function
  4290. * instead of cot_rc_add_js_embed(). Use this way for any sort of static JavaScript or
  4291. * CSS linking.
  4292. *
  4293. * Do not put any private data in any of resource files - it is not secure. If you really need it,
  4294. * then use direct output instead.
  4295. *
  4296. * @global array $cot_rc_reg JavaScript/CSS footer/header resource registry
  4297. * @param string $path Path to a *.js script or *.css stylesheet
  4298. * @param mixed $scope Resource scope. Scope is a selector of domain where resource is used. Valid scopes are:
  4299. * 'global' - global for entire site, will be included everywhere, this is the most static and persistent scope;
  4300. * 'guest' - for unregistered visitors only;
  4301. * 'user' - for registered members only;
  4302. * 'group_123' - for members of a specific group (maingrp), in this example of group with id=123.
  4303. * It is recommended to use 'global' scope whenever possible because it delivers best caching opportunities.
  4304. * @param int $order Order priority number
  4305. * @return bool Returns TRUE normally, FALSE is file was not found
  4306. * @global Cache $cache
  4307. *
  4308. * @deprecated Will be removed in v.1.0. Use Resources::addFile() instead
  4309. */
  4310. function cot_rc_add_file($path, $scope = 'global', $order = 50)
  4311. {
  4312. return Resources::addFile($path, '', $order, $scope);
  4313. }
  4314. /**
  4315. * Registers standard resources
  4316. */
  4317. function cot_rc_add_standard()
  4318. {
  4319. global $cfg;
  4320. if ($cfg['jquery'] && !$cfg['jquery_cdn'])
  4321. {
  4322. Resources::addFile(Resources::jQuery, 'js', 30);
  4323. }
  4324. if ($cfg['jquery'])
  4325. {
  4326. Resources::addFile('js/jqModal.min.js');
  4327. }
  4328. Resources::addFile('js/base.js');
  4329. if ($cfg['jquery'] && $cfg['turnajax'])
  4330. {
  4331. Resources::addFile('js/ajax_on.js');
  4332. }
  4333. }
  4334. /**
  4335. * A shortcut for plain output of an embedded stylesheet/javascript in the header of the page
  4336. *
  4337. * @global array $out Output snippets
  4338. * @param string $code Stylesheet or javascript code
  4339. * @param bool $prepend Prepend this file before other head outputs
  4340. * @param string $type Resource type: 'js' or 'css'
  4341. *
  4342. * @deprecated Will be removed in v.1.0. Resources::embed() instead
  4343. */
  4344. function cot_rc_embed($code, $prepend = false, $type = 'js')
  4345. {
  4346. $order = 60;
  4347. if($prepend) $order = 40;
  4348. Resources::embed($code, $type, $order);
  4349. }
  4350. /**
  4351. * A shortcut for plain output of an embedded stylesheet/javascript in the footer of the page
  4352. *
  4353. * @global array $out Output snippets
  4354. * @param string $code Stylesheet or javascript code
  4355. * @param string $type Resource type: 'js' or 'css'
  4356. *
  4357. * @deprecated Will be removed in v.1.0. Resources::embedFooter() instead
  4358. */
  4359. function cot_rc_embed_footer($code, $type = 'js')
  4360. {
  4361. Resources::embedFooter($code, $type);
  4362. }
  4363. /**
  4364. * Quick link resource pattern
  4365. *
  4366. * @param string $url Link href
  4367. * @param string $text Tag contents
  4368. * @param mixed $attrs Additional attributes as a string or an associative array
  4369. * @return string HTML link
  4370. */
  4371. function cot_rc_link($url, $text, $attrs = '')
  4372. {
  4373. $link_attrs = cot_rc_attr_string($attrs);
  4374. return '<a href="' . $url . '"' . $link_attrs . '>' . $text . '</a>';
  4375. }
  4376. /**
  4377. * A shortcut for plain output of a link to a CSS/JS file in the header of the page
  4378. *
  4379. * @global array $out Output snippets
  4380. * @param string $path Stylesheet *.css or script *.js path/url
  4381. * @param bool $prepend Prepend this file before other header outputs
  4382. *
  4383. * @deprecated Will be removed in v.1.0. Use Resources::linkFile() instead
  4384. */
  4385. function cot_rc_link_file($path, $prepend = false)
  4386. {
  4387. $order = 60;
  4388. if($prepend) $order = 40;
  4389. Resources::linkFile($path, '', $order);
  4390. }
  4391. /**
  4392. * A shortcut to append a JavaScript or CSS file to {FOOTER_JS} tag
  4393. *
  4394. * @global array $out Output snippets
  4395. * @param string $path JavaScript or CSS file path
  4396. *
  4397. * @deprecated Will be removed in v.1.0. Resources::linkFileFooter() instead
  4398. */
  4399. function cot_rc_link_footer($path)
  4400. {
  4401. Resources::linkFileFooter($path);
  4402. }
  4403. /**
  4404. * JS/CSS minification function
  4405. *
  4406. * @param string $code Code to minify
  4407. * @param string $type Type: 'js' or 'css'
  4408. * @return string Minified code
  4409. *
  4410. * @deprecated Will be removed in v.1.0. Resources::linkFileFooter() instead
  4411. */
  4412. function cot_rc_minify($code, $type = 'js')
  4413. {
  4414. return Resources::minify($code, $type);
  4415. }
  4416. /*
  4417. * ========================== Security functions =================================
  4418. */
  4419. /**
  4420. * Generates a captcha
  4421. *
  4422. * @global array $cfg
  4423. * @global array $cot_captcha
  4424. * @param string $use_captcha The CAPTCHA to manually use
  4425. * @return string
  4426. */
  4427. function cot_captcha_generate($use_captcha = '')
  4428. {
  4429. global $cfg, $cot_captcha;
  4430. if(!empty($use_captcha))
  4431. {
  4432. $captcha = $use_captcha;
  4433. }
  4434. elseif (!$cfg['captcharandom'])
  4435. {
  4436. $captcha = $cfg['captchamain'];
  4437. }
  4438. else
  4439. {
  4440. $captcha = $cot_captcha[rand(0, count($cot_captcha) - 1)];
  4441. }
  4442. $tepmcap = '<input type="hidden" name="capman" value="' . $captcha . '" />';
  4443. $captcha .= '_generate';
  4444. if (function_exists($captcha))
  4445. {
  4446. return $captcha() . $tepmcap;
  4447. }
  4448. else
  4449. {
  4450. return '';
  4451. }
  4452. }
  4453. /**
  4454. * Returns the list of currently installed captchas
  4455. * @global array $cot_captcha Captcha registry
  4456. * @return array
  4457. */
  4458. function cot_captcha_list()
  4459. {
  4460. global $cot_captcha;
  4461. return $cot_captcha;
  4462. }
  4463. /**
  4464. * Valides a captcha value
  4465. * @global array $cfg
  4466. * @param string $value Captcha input for validation
  4467. * @return boolean
  4468. */
  4469. function cot_captcha_validate($value)
  4470. {
  4471. // This function can only be called once per request
  4472. static $called = false;
  4473. if ($called)
  4474. {
  4475. return true;
  4476. }
  4477. else
  4478. {
  4479. $called = true;
  4480. }
  4481. $captcha = cot_import('capman', 'P', 'TXT');
  4482. if(!in_array($captcha, cot_captcha_list()))
  4483. {
  4484. return false;
  4485. }
  4486. $captcha .= '_validate';
  4487. if (function_exists($captcha))
  4488. {
  4489. return $captcha($value);
  4490. }
  4491. return true;
  4492. }
  4493. /**
  4494. * Checks GET anti-XSS parameter
  4495. *
  4496. * @param bool $redirect Redirect to message on failure
  4497. * @return bool
  4498. */
  4499. function cot_check_xg($redirect = true)
  4500. {
  4501. global $sys;
  4502. $x = cot_import('x', 'G', 'ALP');
  4503. if ($x != $sys['xk'] && (empty($sys['xk_prev']) || $x != $sys['xk_prev']))
  4504. {
  4505. if ($redirect)
  4506. {
  4507. cot_die_message(950, TRUE);
  4508. }
  4509. return false;
  4510. }
  4511. return true;
  4512. }
  4513. /**
  4514. * Checks POST anti-XSS parameter
  4515. *
  4516. * @return bool
  4517. */
  4518. function cot_check_xp()
  4519. {
  4520. return (defined('COT_NO_ANTIXSS') || defined('COT_AUTH')) ?
  4521. ($_SERVER['REQUEST_METHOD'] == 'POST') : isset($_POST['x']);
  4522. }
  4523. /**
  4524. * Hashes a value with given salt and specified hash algo.
  4525. *
  4526. * @global array $cot_hash_func
  4527. * @param string $data Data to be hash-protected
  4528. * @param string $salt Hashing salt, usually a random value
  4529. * @param string $algo Hashing algo name, must be registered in $cot_hash_funcs
  4530. * @return string Hashed value
  4531. */
  4532. function cot_hash($data, $salt = '', $algo = 'sha256')
  4533. {
  4534. global $cfg, $cot_hash_funcs;
  4535. if (isset($cfg['hashsalt']) && !empty($cfg['hashsalt']))
  4536. {
  4537. // Extra salt for extremely secure sites
  4538. $salt .= $cfg['hashsalt'];
  4539. }
  4540. $func = (in_array($algo, $cot_hash_funcs) && function_exists('cot_hash_' . $algo)) ? 'cot_hash_' . $algo : 'cot_hash_sha256';
  4541. return $func($data, $salt);
  4542. }
  4543. /**
  4544. * Returns the list of available hash algos for use with configs.
  4545. *
  4546. * @global array $cot_hash_func
  4547. * @return array
  4548. */
  4549. function cot_hash_funcs()
  4550. {
  4551. global $cot_hash_funcs;
  4552. return $cot_hash_funcs;
  4553. }
  4554. /**
  4555. * Simple MD5 hash wrapper. Old passwords use this func.
  4556. *
  4557. * @param string $data Data to be hashed
  4558. * @param string $salt Hashing salt, usually a random value
  4559. * @return string MD5 hash of the data
  4560. */
  4561. function cot_hash_md5($data, $salt)
  4562. {
  4563. return md5($data . $salt);
  4564. }
  4565. /**
  4566. * SHA1 hash func for use with cot_hash().
  4567. *
  4568. * @param string $data Data to be hashed
  4569. * @param string $salt Hashing salt, usually a random value
  4570. * @return string SHA1 hash of the data
  4571. */
  4572. function cot_hash_sha1($data, $salt)
  4573. {
  4574. return hash('sha1', $data . $salt);
  4575. }
  4576. /**
  4577. * SHA256 hash func for use with cot_hash(). Default since Cotonti 0.9.11.
  4578. *
  4579. * @param string $data Data to be hashed
  4580. * @param string $salt Hashing salt, usually a random value
  4581. * @return string SHA256 hash of the data
  4582. */
  4583. function cot_hash_sha256($data, $salt)
  4584. {
  4585. return hash('sha256', $data . $salt);
  4586. }
  4587. /**
  4588. * Clears current user action in shield
  4589. */
  4590. function cot_shield_clearaction()
  4591. {
  4592. $_SESSION['shield_action'] = '';
  4593. }
  4594. /**
  4595. * Anti-hammer protection
  4596. *
  4597. * @param int $hammer Hammer rate
  4598. * @param string $action Action type
  4599. * @param int $lastseen User last seen timestamp
  4600. * @return int
  4601. */
  4602. function cot_shield_hammer($hammer, $action, $lastseen)
  4603. {
  4604. global $cfg, $sys;
  4605. if ($action == 'Hammering')
  4606. {
  4607. cot_shield_protect();
  4608. cot_shield_clearaction();
  4609. cot_plugin_active('hits') && cot_stat_inc('totalantihammer');
  4610. }
  4611. if (($sys['now'] - $lastseen) < 4)
  4612. {
  4613. $hammer++;
  4614. if ($hammer > $cfg['shieldzhammer'])
  4615. {
  4616. cot_shield_update(180, 'Hammering');
  4617. cot_log('IP banned 3 mins, was hammering', 'sec');
  4618. $hammer = 0;
  4619. }
  4620. }
  4621. else
  4622. {
  4623. if ($hammer > 0)
  4624. {
  4625. $hammer--;
  4626. }
  4627. }
  4628. return $hammer;
  4629. }
  4630. /**
  4631. * Warn user of shield protection
  4632. *
  4633. */
  4634. function cot_shield_protect()
  4635. {
  4636. global $sys, $shield_limit, $shield_action, $L;
  4637. if ($shield_limit > $sys['now'])
  4638. {
  4639. cot_die_message(403, true, $L['shield_title'], cot_rc('shield_protect', array(
  4640. 'sec' => $shield_limit - $sys['now'],
  4641. 'action' => $shield_action
  4642. )));
  4643. }
  4644. }
  4645. /**
  4646. * Updates shield state
  4647. *
  4648. * @param int $shield_add Hammer
  4649. * @param string $shield_newaction New action type
  4650. */
  4651. function cot_shield_update($shield_add, $shield_newaction)
  4652. {
  4653. global $cfg, $sys;
  4654. $shield_newlimit = $sys['now'] + floor($shield_add * $cfg['shieldtadjust'] /100);
  4655. $_SESSION['online_shield'] = $shield_newlimit;
  4656. $_SESSION['online_action'] = $shield_newaction;
  4657. }
  4658. /**
  4659. * Unregisters globals if globals are On
  4660. * @see http://www.php.net/manual/en/faq.misc.php#faq.misc.registerglobals
  4661. */
  4662. function cot_unregister_globals()
  4663. {
  4664. if (!ini_get('register_globals'))
  4665. {
  4666. return;
  4667. }
  4668. // Might want to change this perhaps to a nicer error
  4669. if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS']))
  4670. {
  4671. die('GLOBALS overwrite attempt detected');
  4672. }
  4673. // Variables that shouldn't be unset
  4674. $noUnset = array('GLOBALS', '_GET',
  4675. '_POST', '_COOKIE',
  4676. '_REQUEST', '_SERVER',
  4677. '_ENV', '_FILES');
  4678. $input = array_merge($_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES,
  4679. isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());
  4680. foreach (array_keys($input) as $k)
  4681. {
  4682. if (!in_array($k, $noUnset) && isset($GLOBALS[$k]))
  4683. {
  4684. unset($GLOBALS[$k]);
  4685. }
  4686. }
  4687. }
  4688. /**
  4689. * Returns XSS protection variable for GET URLs
  4690. *
  4691. * @return string
  4692. */
  4693. function cot_xg()
  4694. {
  4695. global $sys;
  4696. return ('x='.$sys['xk']);
  4697. }
  4698. /**
  4699. * Returns XSS protection field for POST forms
  4700. *
  4701. * @return string
  4702. */
  4703. function cot_xp()
  4704. {
  4705. global $sys;
  4706. return '<div style="display:inline;margin:0;padding:0"><input type="hidden" name="x" value="'.$sys['xk'].'" /></div>';
  4707. }
  4708. /*
  4709. * ============================ URL and URI ===================================
  4710. */
  4711. /**
  4712. * Generates an URL used to confirm an action performed by target URL
  4713. *
  4714. * @param string $target_url Target URL which performs the action
  4715. * @param string $ext_name Module/plugin name to peform the action
  4716. * @param string $msg_code Language string key which contains confirmation request text
  4717. * @return string
  4718. */
  4719. function cot_confirm_url($target_url, $ext_name = '', $msg_key = '')
  4720. {
  4721. global $cfg;
  4722. if ($cfg['confirmlinks'])
  4723. {
  4724. return cot_url('message', array(
  4725. 'msg' => 920,
  4726. 'm' => $ext_name,
  4727. 'lng' => $msg_key,
  4728. 'redirect' => base64_encode($target_url)
  4729. ));
  4730. }
  4731. else
  4732. {
  4733. return $target_url;
  4734. }
  4735. }
  4736. /**
  4737. * Displays redirect page
  4738. *
  4739. * @param string $url Target URI
  4740. */
  4741. function cot_redirect($url)
  4742. {
  4743. global $cfg, $env, $error_string, $sys;
  4744. if (cot_error_found() && $_SERVER['REQUEST_METHOD'] == 'POST')
  4745. {
  4746. // Save the POST data
  4747. if (!empty($error_string))
  4748. {
  4749. // Message should not be lost
  4750. cot_error($error_string);
  4751. }
  4752. cot_import_buffer_save();
  4753. }
  4754. if (!cot_url_check($url))
  4755. {
  4756. // No redirects to foreign domains
  4757. if ($url == '/' || $url == $sys['site_uri'])
  4758. {
  4759. $url = COT_ABSOLUTE_URL;
  4760. }
  4761. else
  4762. {
  4763. if ($url[0] === '/')
  4764. $url = mb_substr($url, 1);
  4765. $url = COT_ABSOLUTE_URL . $url;
  4766. }
  4767. }
  4768. if (defined('COT_AJAX') && COT_AJAX)
  4769. {
  4770. // Save AJAX state, some browsers loose it after redirect (e.g. FireFox 3.6)
  4771. $sep = strpos($url, '?') === false ? '?' : '&';
  4772. $url .= $sep . '_ajax=1';
  4773. }
  4774. if (isset($env['status']))
  4775. {
  4776. $protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
  4777. header($protocol . ' ' . $env['status']);
  4778. }
  4779. if ($cfg['redirmode'])
  4780. {
  4781. $output = $cfg['doctype'].<<<HTM
  4782. <html>
  4783. <head>
  4784. <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  4785. <meta http-equiv="refresh" content="0; url=$url" />
  4786. <title>Redirecting...</title></head>
  4787. <body>Redirecting to <a href="$url">$url</a>
  4788. </body>
  4789. </html>
  4790. HTM;
  4791. header('Refresh: 0; URL='.$url);
  4792. echo $output;
  4793. exit;
  4794. }
  4795. else
  4796. {
  4797. header('Location: '.$url);
  4798. exit;
  4799. }
  4800. }
  4801. /**
  4802. * Splits a query string into keys and values array. In comparison with built-in
  4803. * parse_str() function, this doesn't apply addslashes and urldecode to parameters
  4804. * and does not support arrays and complex parameters.
  4805. *
  4806. * @param string $str Query string
  4807. * @return array
  4808. */
  4809. function cot_parse_str($str)
  4810. {
  4811. $res = array();
  4812. $str = str_replace('&amp;', '&', $str);
  4813. foreach (explode('&', $str) as $item)
  4814. {
  4815. if (!empty($item))
  4816. {
  4817. list($key, $val) = explode('=', $item, 2);
  4818. $res[$key] = $val;
  4819. }
  4820. }
  4821. return $res;
  4822. }
  4823. /**
  4824. * Transforms parameters into URL by following user-defined rules.
  4825. * This function can be overloaded by cot_url_custom().
  4826. *
  4827. * @param string $name Module or script name
  4828. * @param mixed $params URL parameters as array or parameter string
  4829. * @param string $tail URL postfix, e.g. anchor
  4830. * @param bool $htmlspecialchars_bypass If TRUE, will not convert & to &amp; and so on.
  4831. * @param bool $ignore_appendix If TRUE, $cot_url_appendix will be ignored for this URL
  4832. * @return string Valid HTTP URL
  4833. */
  4834. function cot_url($name, $params = '', $tail = '', $htmlspecialchars_bypass = false, $ignore_appendix = false)
  4835. {
  4836. global $cot_url_appendix;
  4837. // Preprocess arguments
  4838. if (is_string($params))
  4839. {
  4840. $params = cot_parse_str($params);
  4841. }
  4842. elseif (!is_array($params))
  4843. {
  4844. $params = array();
  4845. }
  4846. if (!$ignore_appendix && count($cot_url_appendix) > 0)
  4847. {
  4848. $params = $params + $cot_url_appendix;
  4849. }
  4850. foreach ($params as $k => $param)
  4851. {
  4852. if (is_bool($param))
  4853. {
  4854. $params[$k] = (int)$param;
  4855. }
  4856. if (!is_array($param) && !is_object($param))
  4857. {
  4858. $params[$k] = strval($param);
  4859. }
  4860. if ($params[$k] === '')
  4861. {
  4862. unset($params[$k]);
  4863. }
  4864. }
  4865. if (function_exists('cot_url_custom'))
  4866. {
  4867. return cot_url_custom($name, $params, $tail, $htmlspecialchars_bypass);
  4868. }
  4869. $url = in_array($name, array('admin', 'login', 'message')) ? "$name.php" : 'index.php';
  4870. if (!in_array($name, array('admin', 'index', 'login', 'message', 'plug')))
  4871. {
  4872. $params = array('e' => $name) + $params;
  4873. }
  4874. // Append query string if needed
  4875. if (count($params) > 0)
  4876. {
  4877. $sep = $htmlspecialchars_bypass ? '&' : '&amp;';
  4878. if (version_compare(PHP_VERSION, '5.4.0', '>='))
  4879. {
  4880. $url .= '?' . http_build_query($params, '', $sep, PHP_QUERY_RFC3986);
  4881. }
  4882. else
  4883. {
  4884. $url .= '?' . str_replace('+', '%20', http_build_query($params, '', $sep));
  4885. }
  4886. }
  4887. $url .= $tail;
  4888. //$url = str_replace('&amp;amp;', '&amp;', $url);
  4889. return $url;
  4890. }
  4891. /**
  4892. * Constructs a modified version of a current URL.
  4893. * @param array $params Modified params
  4894. * @param string $tail URL postfix, e.g. anchor
  4895. * @param bool $htmlspecialchars_bypass If TRUE, will not convert & to &amp; and so on.
  4896. * @param bool $ignore_appendix If TRUE, $cot_url_appendix will be ignored for this URL
  4897. * @return string Valid HTTP URL
  4898. */
  4899. function cot_url_modify($params = array(), $tail = '', $htmlspecialchars_bypass = false, $ignore_appendix = false)
  4900. {
  4901. // Preprocess arguments
  4902. if (is_string($params))
  4903. {
  4904. $params = cot_parse_str($params);
  4905. }
  4906. if (!is_array($params))
  4907. {
  4908. $params = array();
  4909. }
  4910. $area = defined('COT_PLUG') ? 'plug' : cot::$env['ext'];
  4911. $params = array_merge($_GET, $params);
  4912. if (!defined('COT_PLUG'))
  4913. {
  4914. unset($params['e']);
  4915. }
  4916. unset($params['rwr']);
  4917. return cot_url(
  4918. $area,
  4919. $params,
  4920. $tail,
  4921. $htmlspecialchars_bypass,
  4922. $ignore_appendix
  4923. );
  4924. }
  4925. /**
  4926. * Checks if an absolute URL belongs to current site or its subdomains
  4927. *
  4928. * @param string $url Absolute URL
  4929. * @return bool
  4930. */
  4931. function cot_url_check($url)
  4932. {
  4933. global $sys;
  4934. return preg_match('`^'.preg_quote($sys['scheme'].'://').'([\w\p{L}\.\-]+\.)?'.preg_quote($sys['domain']).'`ui', $url);
  4935. }
  4936. /**
  4937. * Store URI-redir to session
  4938. *
  4939. * @global $sys
  4940. */
  4941. function cot_uriredir_store()
  4942. {
  4943. global $sys;
  4944. $m = cot_import('m', 'G', 'ALP');
  4945. if ($_SERVER['REQUEST_METHOD'] != 'POST' // not form action/POST
  4946. && empty($_GET['x']) // not xg, hence not form action/GET and not command from GET
  4947. && !defined('COT_MESSAGE') // not message location
  4948. && !defined('COT_AUTH') // not login/logout location
  4949. && (!defined('COT_USERS')
  4950. || is_null($m)
  4951. || !in_array($m, array('auth', 'logout', 'register'))
  4952. )
  4953. )
  4954. {
  4955. $_SESSION['s_uri_redir'] = $sys['uri_redir'];
  4956. }
  4957. }
  4958. /**
  4959. * Splits URL for its parts
  4960. * Same as `parse_str` but with workaround for URL with omitted scheme for old PHP versions
  4961. *
  4962. * @param array $url Array of URL parts
  4963. * @see http://php.net/manual/en/function.parse-str.php
  4964. */
  4965. function cot_parse_url($url)
  4966. {
  4967. $urlp = parse_url($url);
  4968. $needfix = false;
  4969. // check for URL with omited scheme on PHP prior 5.4.7 (//somesite.com)
  4970. if (substr($urlp['path'],0,2) == '//' && empty($urlp['scheme'])) $needfix = true;
  4971. // check for URL with auth credentials (user[:pass]@site.com/)
  4972. if (empty($urlp['host']) && preg_match('#^(([^@:]+)|([^@:]+:[^@:]+?))@.+/#', $urlp['path'])) $needfix = true;
  4973. if ($needfix) {
  4974. $fake_scheme = 'fix-url-parsing';
  4975. $delimiter = (substr($urlp['path'],0,2) == '//') ? ':' : '://';
  4976. $url = $fake_scheme . $delimiter . $url; // adding fake scheme
  4977. $urlp = parse_url($url);
  4978. if ($urlp['scheme'] == $fake_scheme) unset($urlp['scheme']);
  4979. }
  4980. return $urlp;
  4981. }
  4982. /**
  4983. * Builds URL string from URL parts
  4984. *
  4985. * @param array $urlp
  4986. * @return string URL Array of URL parts
  4987. * @see `cot_parse_url()`
  4988. */
  4989. function cot_http_build_url($urlp)
  4990. {
  4991. $url = '';
  4992. $port = '';
  4993. if(!empty($urlp['port'])) {
  4994. $port = (string)intval($urlp['port']);
  4995. if($urlp['port'] != $port) $port = '';
  4996. }
  4997. if (!empty($urlp['scheme'])) $url .= $urlp['scheme'] . '://';
  4998. if (!empty($urlp['user'])) {
  4999. if (!empty($urlp['pass'])) {
  5000. $url .= $urlp['user'] . ':' . $urlp['pass'] . '@';
  5001. } else{
  5002. $url .= $urlp['user'] . '@';
  5003. }
  5004. }
  5005. if (!empty($urlp['host'])) $url .= $urlp['host'];
  5006. if ($port && $port != '80' && preg_match('/^\d+$/', $port)) $url .= ':' . $port;
  5007. if ( (empty($urlp['path']) && ( !empty($urlp['query']) || !empty($urlp['fragment']) ))
  5008. || ((!empty($urlp['path'])) && substr($urlp['path'], 0, 1) != '/') ) $urlp['path'] = '/' . $urlp['path'];
  5009. if (!empty($urlp['path'])) $url .= $urlp['path'];
  5010. if (!empty($urlp['query'])) $url .= '?' . $urlp['query'];
  5011. if (!empty($urlp['fragment'])) $url .= '#' . $urlp['fragment'];
  5012. return $url;
  5013. }
  5014. /**
  5015. * Sanitize given URL to prevent XSS
  5016. *
  5017. * @param string $url URL to process (absolute or not)
  5018. */
  5019. function cot_url_sanitize($url)
  5020. {
  5021. function urlfilter($str)
  5022. {
  5023. return rawurlencode(rawurldecode($str));
  5024. }
  5025. $urlp = cot_parse_url($url);
  5026. $urlp['fragment'] = !empty($urlp['fragment']) ? urlfilter($urlp['fragment']) : '';
  5027. $path = $urlp['path'];
  5028. $query = !empty($urlp['query']) ? str_replace('&amp;', '&', $urlp['query']) : '';
  5029. $path = explode('/', $path);
  5030. $path = array_map('urlfilter', $path);
  5031. $urlp['path'] = implode('/', $path);
  5032. $filtered_params = array();
  5033. foreach (explode('&', $query) as $item)
  5034. {
  5035. if (!empty($item))
  5036. {
  5037. list($key, $val) = explode('=', $item, 2);
  5038. $filtered_params[] = urlfilter($key) . '=' . urlfilter($val);
  5039. }
  5040. }
  5041. if (sizeof($filtered_params)) $urlp['query'] = implode('&', $filtered_params);
  5042. return cot_http_build_url($urlp);
  5043. }
  5044. /**
  5045. * Apply URI-redir that stored in session
  5046. *
  5047. * @param bool $cfg_redir Configuration of redirect back
  5048. * @global $redirect
  5049. */
  5050. function cot_uriredir_apply($cfg_redir = true)
  5051. {
  5052. global $redirect;
  5053. if ($cfg_redir && empty($redirect) && !empty($_SESSION['s_uri_redir']))
  5054. {
  5055. $redirect = $_SESSION['s_uri_redir'];
  5056. }
  5057. }
  5058. /**
  5059. * Checks URI-redir for xg before redirect
  5060. *
  5061. * @param string $uri Target URI
  5062. */
  5063. function cot_uriredir_redirect($uri)
  5064. {
  5065. if (mb_strpos($uri, '&x=') !== false || mb_strpos($uri, '?x=') !== false)
  5066. {
  5067. $uri = cot_url('index'); // xg, not redirect to form action/GET or to command from GET
  5068. }
  5069. cot_redirect($uri);
  5070. }
  5071. /*
  5072. * ========================= Internationalization (i18n) ======================
  5073. */
  5074. $cot_languages['cn']= '中文';
  5075. $cot_languages['de']= 'Deutsch';
  5076. $cot_languages['dk']= 'Dansk';
  5077. $cot_languages['en']= 'English';
  5078. $cot_languages['es']= 'Español';
  5079. $cot_languages['fi']= 'Suomi';
  5080. $cot_languages['fr']= 'Français';
  5081. $cot_languages['gr']= 'Greek';
  5082. $cot_languages['hu']= 'Hungarian';
  5083. $cot_languages['it']= 'Italiano';
  5084. $cot_languages['jp']= '日本語';
  5085. $cot_languages['kr']= '한국어';
  5086. $cot_languages['nl']= 'Dutch';
  5087. $cot_languages['pl']= 'Polski';
  5088. $cot_languages['pt']= 'Portugese';
  5089. $cot_languages['ru']= 'Русский';
  5090. $cot_languages['se']= 'Svenska';
  5091. $cot_languages['ua'] = 'Українська';
  5092. /**
  5093. * Transliterates a string if transliteration is available
  5094. *
  5095. * @param string $str Source string
  5096. * @return string
  5097. */
  5098. function cot_translit_encode($str)
  5099. {
  5100. global $lang, $cot_translit;
  5101. static $lang_loaded = false;
  5102. if (!$lang_loaded && $lang != 'en' && file_exists(cot_langfile('translit', 'core')))
  5103. {
  5104. require_once cot_langfile('translit', 'core');
  5105. $lang_loaded = true;
  5106. }
  5107. if (is_array($cot_translit))
  5108. {
  5109. // Apply transliteration
  5110. $str = strtr($str, $cot_translit);
  5111. }
  5112. return $str;
  5113. }
  5114. /**
  5115. * Backwards transition for cot_translit_encode
  5116. *
  5117. * @param string $str Encoded string
  5118. * @return string
  5119. */
  5120. function cot_translit_decode($str)
  5121. {
  5122. global $lang, $cot_translitb;
  5123. static $lang_loaded = false;
  5124. if (!$lang_loaded && $lang != 'en' && file_exists(cot_langfile('translit', 'core')))
  5125. {
  5126. require_once cot_langfile('translit', 'core');
  5127. $lang_loaded = true;
  5128. }
  5129. if (is_array($cot_translitb))
  5130. {
  5131. // Apply transliteration
  5132. $str = strtr($str, $cot_translitb);
  5133. }
  5134. return $str;
  5135. }
  5136. /**
  5137. * Makes correct plural forms of words
  5138. *
  5139. * @global string $lang Current language
  5140. * @param int $digit Numeric value
  5141. * @param mixed $expr Word or expression
  5142. * @param bool $onlyword Return only words, without numbers
  5143. * @param bool $canfrac - Numeric value can be Decimal Fraction
  5144. * @return string
  5145. */
  5146. function cot_declension($digit, $expr, $onlyword = false, $canfrac = false)
  5147. {
  5148. global $lang, $Ls;
  5149. $expr = is_string($expr) && isset($Ls[$expr]) ? $Ls[$expr] : $expr;
  5150. if (is_string($expr) && mb_strpos($expr, ',') !== false)
  5151. {
  5152. $expr = preg_split('#\s*,\s*#', $expr);
  5153. }
  5154. if (!is_array($expr))
  5155. {
  5156. return trim(($onlyword ? '' : "$digit ").$expr);
  5157. }
  5158. $is_frac = false;
  5159. if ($canfrac)
  5160. {
  5161. if ((is_float($digit) && $digit!=floor($digit)) || mb_strpos($digit, '.') !== false)
  5162. {
  5163. $i = floatval($digit);
  5164. $is_frac = true;
  5165. }
  5166. else
  5167. {
  5168. $i = intval($digit);
  5169. }
  5170. }
  5171. else
  5172. {
  5173. $i = intval(preg_replace('#\D+#', '', $digit));
  5174. }
  5175. $plural = cot_get_plural($i, $lang, $is_frac);
  5176. $cnt = count($expr);
  5177. return trim(($onlyword ? '' : "$digit ").(($cnt > 0 && $plural < $cnt) ? $expr[$plural] : ''));
  5178. }
  5179. /**
  5180. * Used in cot_declension to get rules for concrete languages
  5181. *
  5182. * @param int $plural Numeric value
  5183. * @param string $lang Target language code
  5184. * @param bool $is_frac true if numeric value is fraction, otherwise false
  5185. * @return int
  5186. */
  5187. function cot_get_plural($plural, $lang, $is_frac = false)
  5188. {
  5189. switch ($lang)
  5190. {
  5191. case 'en':
  5192. case 'de':
  5193. case 'nl':
  5194. case 'se':
  5195. case 'us':
  5196. return ($plural == 1) ? 1 : 0;
  5197. case 'fr':
  5198. return ($plural > 1) ? 0 : 1;
  5199. case 'ru':
  5200. case 'ua':
  5201. if ($is_frac)
  5202. {
  5203. return 1;
  5204. }
  5205. $plural %= 100;
  5206. return (5 <= $plural && $plural <= 20) ? 2 : ((1 == ($plural %= 10)) ? 0 : ((2 <= $plural && $plural <= 4) ? 1 : 2));
  5207. default:
  5208. return 0;
  5209. }
  5210. }
  5211. /*
  5212. * ============================================================================
  5213. */
  5214. if (isset($cfg['customfuncs']) && $cfg['customfuncs'])
  5215. {
  5216. require_once $cfg['system_dir'] . '/functions.custom.php';
  5217. }