2 // +----------------------------------------------------------------------+
 
   3 // | Anuko Time Tracker
 
   4 // +----------------------------------------------------------------------+
 
   5 // | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
 
   6 // +----------------------------------------------------------------------+
 
   7 // | LIBERAL FREEWARE LICENSE: This source code document may be used
 
   8 // | by anyone for any purpose, and freely redistributed alone or in
 
   9 // | combination with other software, provided that the license is obeyed.
 
  11 // | There are only two ways to violate the license:
 
  13 // | 1. To redistribute this code in source form, with the copyright
 
  14 // |    notice or license removed or altered. (Distributing in compiled
 
  15 // |    forms without embedded copyright notices is permitted).
 
  17 // | 2. To redistribute modified versions of this code in *any* form
 
  18 // |    that bears insufficient indications that the modifications are
 
  19 // |    not the work of the original author(s).
 
  21 // | This license applies to this document only, not any other software
 
  22 // | that it may be combined with.
 
  24 // +----------------------------------------------------------------------+
 
  26 // | https://www.anuko.com/time_tracker/credits.htm
 
  27 // +----------------------------------------------------------------------+
 
  29 // import() function loads a class.
 
  30 function import($class_name) {
 
  32     dirname($_SERVER["SCRIPT_FILENAME"]),
 
  36             $pos = strpos($class_name, ".");
 
  37         if (!($pos === false)) {
 
  38             $peaces = explode(".", $class_name);
 
  40             for ($i=0; $i<count($peaces)-1; $i++) {
 
  41                 $p = $p . "/" . $peaces[$i];
 
  43                         $libs = array_merge(array(LIBRARY_DIR . $p),$libs);
 
  44             $class_name = $peaces[count($peaces)-1];
 
  47                 $filename = $class_name . '.class.php';
 
  49                 foreach($libs as $lib) {
 
  50                         $inc_filename = $lib . '/' . $filename;
 
  51                         if (file_exists($inc_filename)) {
 
  52                                         require_once($inc_filename);
 
  57                 print '<br><b>load_class: error loading file "'.$filename.'"</b>';
 
  61         // The mu_sort function is used to sort a multi-dimensional array.
 
  62         // It looks like the code example is taken from the PHP manual http://ca2.php.net/manual/en/function.sort.php
 
  63         function mu_sort($array, $key_sort) {
 
  65                 if (!is_array($array) || count($array)==0)
 
  68                 $key_sorta = explode(",", $key_sort);
 
  69                 $keys = array_keys($array[0]);
 
  71                 for($m=0; $m < count($key_sorta); $m++) {
 
  72                         $nkeys[$m] = trim($key_sorta[$m]);
 
  74                 $n += count($key_sorta);
 
  76                 for($i=0; $i < count($keys); $i++) {
 
  77                         if(!in_array($keys[$i], $key_sorta)) {
 
  78                                 $nkeys[$n] = $keys[$i];
 
  83                 for($u=0;$u<count($array); $u++) {
 
  85                         for($s=0; $s<count($nkeys); $s++) {
 
  87                                 $output[$u][$k] = $array[$u][$k];
 
  97          * @param unknown $value
 
 100         function toFloat($value) {
 
 101                 if (isset($value) && (strlen($value) > 0)) {
 
 102                         $value = str_replace(",",".",$value);
 
 103                         return floatval($value);
 
 108         function stripslashes_deep($value) {
 
 109             $value = is_array($value) ?
 
 110                 array_map('stripslashes_deep', $value) :
 
 111                 stripslashes($value);
 
 115         function &getConnection() {
 
 116         if (!isset($GLOBALS["_MDB2_CONNECTION"])) {
 
 118                 require_once('MDB2.php');
 
 120                 $mdb2 = MDB2::connect(DSN);
 
 121                         if (is_a($mdb2, 'PEAR_Error')) {
 
 122                         die($mdb2->getMessage());
 
 125                         $mdb2->setFetchMode(MDB2_FETCHMODE_ASSOC);
 
 127                         $GLOBALS["_MDB2_CONNECTION"] = $mdb2;
 
 129         return $GLOBALS["_MDB2_CONNECTION"];
 
 133 // time_to_decimal converts a time string such as 1:15 to its decimal representation such as 1.25 or 1,25.
 
 134 function time_to_decimal($val) {
 
 136   $parts = explode(':', $val); // parts[0] is hours, parts[1] is minutes.
 
 138   $minutePercent = round($parts[1]*100/60); // Integer value (0-98) of percent of minutes portion in the hour.
 
 139   if($minutePercent < 10) $minutePercent = '0'.$minutePercent; // Pad small values with a 0 to always have 2 digits.
 
 141   $decimalTime = $parts[0].$user->decimal_mark.$minutePercent; // Construct decimal representation of time value.
 
 146 function magic_quotes_off()
 
 148   $_POST = array_map('stripslashes_deep', $_POST);
 
 149   $_GET = array_map('stripslashes_deep', $_GET);
 
 150   $_COOKIE = array_map('stripslashes_deep', $_COOKIE);
 
 153 // check_extension checks whether a required PHP extension is loaded and dies if not so.
 
 154 function check_extension($ext)
 
 156   if (!extension_loaded($ext))
 
 157     die("PHP extension '{$ext}' is required but is not loaded. Read Time Tracker Install Guide for help.");
 
 160 // isTrue is a helper function to return correct false for older config.php values defined as a string 'false'.
 
 161 function isTrue($val)
 
 163   return (defined($val) && constant($val) === true);
 
 166 // ttValidString is used to check user input to validate a string.
 
 167 function ttValidString($val, $emptyValid = false)
 
 170   if (strlen($val) == 0 && !$emptyValid)
 
 173   // String must not be XSS evil (to insert JavaScript).
 
 174   if (stristr($val, '<script>') || stristr($val, '<script '))
 
 180 // ttValidTemplateText is used to check template-based user input.
 
 181 // When templates are used, required input parts must be filled by user.
 
 182 // We identify these parts by 3 "stop sign" emojis (aka "octagonal sign" U+1F6D1).
 
 183 function ttValidTemplateText($val)
 
 185   $valid = strpos($val, '🛑🛑🛑') === false; // no 3 "stop sign" emojis in a row.
 
 189 // ttValidEmail is used to check user input to validate an email string.
 
 190 function ttValidEmail($val, $emptyValid = false)
 
 193   if (strlen($val) == 0)
 
 194     return ($emptyValid ? true : false);
 
 196   // String must not be XSS evil (to insert JavaScript).
 
 197   if (stristr($val, '<script>') || stristr($val, '<script '))
 
 200   // Validate a single email address. TODO: improve for compliancy with RFC.
 
 201   if (!preg_match("/^[_a-zA-Z\d\'-\.]+@([_a-zA-Z\d\-]+(\.[_a-zA-Z\d\-]+)+)$/", $val))
 
 207 // ttValidEmailList is used to check user input to validate an email string.
 
 208 function ttValidEmailList($val, $emptyValid = false)
 
 211   if (strlen($val) == 0)
 
 212     return ($emptyValid ? true : false);
 
 214   // String must not be XSS evil (to insert JavaScript).
 
 215   if (stristr($val, '<script>') || stristr($val, '<script '))
 
 218   // Validates a list of email addresses separated by a comma with optional spaces.
 
 219   if (!preg_match("/^[_a-zA-Z\d\'-\.]+@([_a-zA-Z\d\-]+(\.[_a-zA-Z\d\-]+)+)(,\s*[_a-zA-Z\d\'-\.]+@([_a-zA-Z\d\-]+(\.[_a-zA-Z\d\-]+)+))*$/", $val))
 
 225 // ttValidFloat is used to check user input to validate a float value.
 
 226 function ttValidFloat($val, $emptyValid = false)
 
 229   if (strlen($val) == 0)
 
 230     return ($emptyValid ? true : false);
 
 233   $decimal = $user->getDecimalMark();
 
 235   if (!preg_match('/^-?[0-9'.$decimal.']+$/', $val))
 
 241 // ttValidDate is used to check user input to validate a date.
 
 242 function ttValidDate($val)
 
 245   if (strlen($val) == 0)
 
 248   // This should accept a string in format 'YYYY-MM-DD', 'MM/DD/YYYY', 'DD.MM.YYYY', or 'DD.MM.YYYY whatever'.
 
 249   if (!preg_match('/^\d\d\d\d-\d\d-\d\d$/', $val) &&
 
 250     !preg_match('/^\d\d\/\d\d\/\d\d\d\d$/', $val) &&
 
 251     !preg_match('/^\d\d\.\d\d\.\d\d\d\d$/', $val) &&
 
 252     !preg_match('/^\d\d\.\d\d\.\d\d\d\d .+$/', $val))
 
 258 // ttValidInteger is used to check user input to validate an integer.
 
 259 function ttValidInteger($val, $emptyValid = false)
 
 262   if (strlen($val) == 0)
 
 263     return ($emptyValid ? true : false);
 
 265   if (!preg_match('/^[0-9]+$/', $val))
 
 271 // ttValidCronSpec is used to check user input to validate cron specification.
 
 272 function ttValidCronSpec($val)
 
 274   // This code is adapted from http://stackoverflow.com/questions/235504/validating-crontab-entries-w-php
 
 277      'hour'=>'[01]?\d|2[0-3]',
 
 278      'day'=>'0?[1-9]|[12]\d|3[01]',
 
 279      'month'=>'[1-9]|1[012]',
 
 283   foreach($numbers as $field=>$number) {
 
 284     $range= "($number)(-($number)(\/\d+)?)?";
 
 285     $field_re[$field]= "\*(\/\d+)?|$range(,$range)*";
 
 288   $field_re['month'].='|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec';
 
 289   $field_re['dow'].='|mon|tue|wed|thu|fri|sat|sun';
 
 291   $fields_re= '('.join(')\s+(', $field_re).')';
 
 294   $replacements= '@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly';
 
 301                 "|($replacements)\s+\S".
 
 304   // The above block from the link did not work for me.
 
 307   $regexp = '/^'.$fields_re.'$/';
 
 309   if (!preg_match($regexp, $val))
 
 315 // ttValidCondition is used to check user input to validate a notification condition.
 
 316 function ttValidCondition($val, $emptyValid = true)
 
 319   if (strlen($val) == 0)
 
 320     return ($emptyValid ? true : false);
 
 322   // String must not be XSS evil (to insert JavaScript).
 
 323   if (stristr($val, '<script>') || stristr($val, '<script '))
 
 326   if (!preg_match("/^count\s?(=|[<>]=?|<>)\s?\d+$/", $val))
 
 332 // ttValidIP is used to check user input to validate a comma-separated
 
 333 // list of IP subnet "prefixes", for example 192.168.0 (note: no .* in the end).
 
 334 // We keep regexp checks here simple - they are not precise.
 
 335 // For example, IPv4-mapped IPv6 addresses will fail. This may need to be fixed.
 
 336 function ttValidIP($val, $emptyValid = false)
 
 339   if (strlen($val) == 0 && $emptyValid)
 
 342   $subnets = explode(',', $val);
 
 343   foreach ($subnets as $subnet) {
 
 344     $ipv4 = preg_match('/^\d\d?\d?(\.\d\d?\d?){0,3}\.?$/', $subnet); // Not precise check.
 
 345     $ipv6 = preg_match('/^([0-9a-fA-F]{4})(:[0-9a-fA-F]{4}){0,7}$/', $subnet); // Not precise check.
 
 346     if (!$ipv4 && !$ipv6)
 
 352 // ttAccessAllowed checks whether user is allowed access to a particular page.
 
 353 // It is used as an initial check on all publicly available pages
 
 354 // (except login.php, register.php, and others where we don't have to check).
 
 355 function ttAccessAllowed($required_right)
 
 360   // Redirect to login page if user is not authenticated.
 
 361   if (!$auth->isAuthenticated()) {
 
 362     header('Location: login.php');
 
 366   // Check IP restriction, if set.
 
 367   if ($user->allow_ip && !$user->can('override_allow_ip')) {
 
 368     $access_allowed = false;
 
 369     $user_ip = $_SERVER['REMOTE_ADDR'];
 
 370     $allowed_ip_array = explode(',', $user->allow_ip);
 
 371     foreach ($allowed_ip_array as $allowed_ip) {
 
 372       $len = strlen($allowed_ip);
 
 373       if (substr($user_ip, 0, $len) === $allowed_ip) { // startsWith check.
 
 374          $access_allowed = true;
 
 378     if (!$access_allowed) return false;
 
 381   // Check if user has the right.
 
 382   if (in_array($required_right, $user->rights)) {
 
 383     import('ttUserHelper');
 
 384     ttUserHelper::updateLastAccess();
 
 391 // ttStartsWith functions checks if a string starts with a given substring.
 
 392 function ttStartsWith($string, $startString)
 
 394     $len = strlen($startString);
 
 395     return (substr($string, 0, $len) === $startString);
 
 398 // ttEndsWith functions checks if a string ends with a given substring.
 
 399 function ttEndsWith($string, $endString)
 
 401     $len = strlen($endString);
 
 402     if ($len == 0) return true;
 
 403     return (substr($string, -$len) === $endString);
 
 406 // ttDateToUserFormat converts a date from database format to user format.
 
 407 function ttDateToUserFormat($date)
 
 410   $o_date = new DateAndTime(DB_DATEFORMAT, $date);
 
 411   return $o_date->toString($user->date_format);
 
 414 // ttRandomString generates a random alphanumeric string.
 
 415 function ttRandomString($length = 32) {
 
 416   $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
 
 417   $charactersLength = strlen($characters);
 
 419   for ($i = 0; $i < $length; $i++) {
 
 420     $randomString .= $characters[rand(0, $charactersLength - 1)];
 
 422   return $randomString;