PageRenderTime 58ms CodeModel.GetById 18ms RepoModel.GetById 1ms 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

Large files files are truncated, but you can click here to view the full file

  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 …

Large files files are truncated, but you can click here to view the full file