e2950753ec4e208475822050253aa0f122cf763b
[timetracker.git] / WEB-INF / lib / common.lib.php
1 <?php
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.
10 // |
11 // | There are only two ways to violate the license:
12 // |
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).
16 // |
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).
20 // |
21 // | This license applies to this document only, not any other software
22 // | that it may be combined with.
23 // |
24 // +----------------------------------------------------------------------+
25 // | Contributors:
26 // | https://www.anuko.com/time_tracker/credits.htm
27 // +----------------------------------------------------------------------+
28
29 // import() function loads a class.
30 function import($class_name) {
31   $libs = array(
32     dirname($_SERVER["SCRIPT_FILENAME"]),
33     LIBRARY_DIR
34   );
35
36             $pos = strpos($class_name, ".");
37         if (!($pos === false)) {
38             $peaces = explode(".", $class_name);
39             $p = "";
40             for ($i=0; $i<count($peaces)-1; $i++) {
41                 $p = $p . "/" . $peaces[$i];
42             }
43                         $libs = array_merge(array(LIBRARY_DIR . $p),$libs);
44             $class_name = $peaces[count($peaces)-1];
45         }
46
47                 $filename = $class_name . '.class.php';
48
49                 foreach($libs as $lib) {
50                         $inc_filename = $lib . '/' . $filename;
51                         if (file_exists($inc_filename)) {
52                                         require_once($inc_filename);
53                                         return $class_name;
54                         }
55                 }
56
57                 print '<br><b>load_class: error loading file "'.$filename.'"</b>';
58                 die();
59 }
60
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) {
64                 $n = 0;
65                 if (!is_array($array) || count($array)==0)
66                         return array();
67
68                 $key_sorta = explode(",", $key_sort);
69                 $keys = array_keys($array[0]);
70
71                 for($m=0; $m < count($key_sorta); $m++) {
72                         $nkeys[$m] = trim($key_sorta[$m]);
73                 }
74                 $n += count($key_sorta);
75
76                 for($i=0; $i < count($keys); $i++) {
77                         if(!in_array($keys[$i], $key_sorta)) {
78                                 $nkeys[$n] = $keys[$i];
79                                 $n += "1";
80                         }
81                 }
82
83                 for($u=0;$u<count($array); $u++) {
84                         $arr = $array[$u];
85                         for($s=0; $s<count($nkeys); $s++) {
86                                 $k = $nkeys[$s];
87                                 $output[$u][$k] = $array[$u][$k];
88                         }
89                 }
90                 sort($output);
91                 return $output;
92         }
93
94         /**
95          * return float type
96          *
97          * @param unknown $value
98          * @return unknown
99          */
100         function toFloat($value) {
101                 if (isset($value) && (strlen($value) > 0)) {
102                         $value = str_replace(",",".",$value);
103                         return floatval($value);
104                 }
105                 return null;
106         }
107
108         function stripslashes_deep($value) {
109             $value = is_array($value) ?
110                 array_map('stripslashes_deep', $value) :
111                 stripslashes($value);
112         return $value;
113         }
114
115         function &getConnection() {
116         if (!isset($GLOBALS["_MDB2_CONNECTION"])) {
117
118                 require_once('MDB2.php');
119
120                 $mdb2 = MDB2::connect(DSN);
121                         if (is_a($mdb2, 'PEAR_Error')) {
122                         die($mdb2->getMessage());
123                         }
124
125                         $mdb2->setFetchMode(MDB2_FETCHMODE_ASSOC);
126                         
127                         $GLOBALS["_MDB2_CONNECTION"] = $mdb2;
128         }
129         return $GLOBALS["_MDB2_CONNECTION"];
130         }
131
132
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) {
135   global $user;
136   $parts = explode(':', $val); // parts[0] is hours, parts[1] is minutes.
137
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.
140
141   $decimalTime = $parts[0].$user->decimal_mark.$minutePercent; // Construct decimal representation of time value.
142
143   return $decimalTime;
144 }
145
146 function sec_to_time_fmt_hm($sec)
147 {
148   return sprintf("%d:%02d", $sec / 3600, $sec % 3600 / 60);
149 }
150
151 function magic_quotes_off()
152 {
153   $_POST = array_map('stripslashes_deep', $_POST);
154   $_GET = array_map('stripslashes_deep', $_GET);
155   $_COOKIE = array_map('stripslashes_deep', $_COOKIE);
156 }
157
158 // check_extension checks whether a required PHP extension is loaded and dies if not so.
159 function check_extension($ext)
160 {
161   if (!extension_loaded($ext))
162     die("PHP extension '{$ext}' is required but is not loaded. Read Time Tracker Install Guide for help.");
163 }
164
165 // isTrue is a helper function to return correct false for older config.php values defined as a string 'false'.
166 function isTrue($val)
167 {
168   return (defined($val) && constant($val) === true);
169 }
170
171 // ttValidString is used to check user input to validate a string.
172 function ttValidString($val, $emptyValid = false)
173 {
174   $val = trim($val);
175   if (strlen($val) == 0 && !$emptyValid)
176     return false;
177     
178   // String must not be XSS evil (to insert JavaScript).
179   if (stristr($val, '<script>') || stristr($val, '<script '))
180     return false;
181     
182   return true;    
183 }
184
185 // ttValidTemplateText is used to check template-based user input.
186 // When templates are used, required input parts must be filled by user.
187 // We identify these parts by 3 "stop sign" emojis (aka "octagonal sign" U+1F6D1).
188 function ttValidTemplateText($val)
189 {
190   $valid = strpos($val, '🛑🛑🛑') === false; // no 3 "stop sign" emojis in a row.
191   return $valid;
192 }
193
194 // ttValidEmail is used to check user input to validate an email string.
195 function ttValidEmail($val, $emptyValid = false)
196 {
197   $val = trim($val);
198   if (strlen($val) == 0)
199     return ($emptyValid ? true : false);
200         
201   // String must not be XSS evil (to insert JavaScript).
202   if (stristr($val, '<script>') || stristr($val, '<script '))
203     return false;
204     
205   // Validate a single email address. TODO: improve for compliancy with RFC.
206   if (!preg_match("/^[_a-zA-Z\d\'-\.]+@([_a-zA-Z\d\-]+(\.[_a-zA-Z\d\-]+)+)$/", $val))
207     return false;
208   
209   return true;    
210 }
211
212 // ttValidEmailList is used to check user input to validate an email string.
213 function ttValidEmailList($val, $emptyValid = false)
214 {
215   $val = trim($val);
216   if (strlen($val) == 0)
217     return ($emptyValid ? true : false);
218         
219   // String must not be XSS evil (to insert JavaScript).
220   if (stristr($val, '<script>') || stristr($val, '<script '))
221     return false;
222     
223   // Validates a list of email addresses separated by a comma with optional spaces.
224   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     return false;
226     
227   return true;
228 }
229
230 // ttValidFloat is used to check user input to validate a float value.
231 function ttValidFloat($val, $emptyValid = false)
232 {
233   $val = trim($val);
234   if (strlen($val) == 0)
235     return ($emptyValid ? true : false);
236     
237   global $user;
238   $decimal = $user->getDecimalMark();
239         
240   if (!preg_match('/^-?[0-9'.$decimal.']+$/', $val))
241     return false;
242     
243   return true;    
244 }
245
246 // ttValidDate is used to check user input to validate a date.
247 function ttValidDate($val)
248 {
249   $val = trim($val);
250   if (strlen($val) == 0)
251     return false;
252
253   // This should accept a string in format 'YYYY-MM-DD', 'MM/DD/YYYY', 'DD.MM.YYYY', or 'DD.MM.YYYY whatever'.
254   if (!preg_match('/^\d\d\d\d-\d\d-\d\d$/', $val) &&
255     !preg_match('/^\d\d\/\d\d\/\d\d\d\d$/', $val) &&
256     !preg_match('/^\d\d\.\d\d\.\d\d\d\d$/', $val) &&
257     !preg_match('/^\d\d\.\d\d\.\d\d\d\d .+$/', $val))
258     return false;
259     
260   return true;    
261 }
262
263 // ttValidInteger is used to check user input to validate an integer.
264 function ttValidInteger($val, $emptyValid = false)
265 {
266   $val = trim($val);
267   if (strlen($val) == 0)
268     return ($emptyValid ? true : false);
269     
270   if (!preg_match('/^[0-9]+$/', $val))
271     return false;
272
273   return true;
274 }
275
276 // ttValidCronSpec is used to check user input to validate cron specification.
277 function ttValidCronSpec($val)
278 {
279   // This code is adapted from http://stackoverflow.com/questions/235504/validating-crontab-entries-w-php
280   $numbers= array(
281      'min'=>'[0-5]?\d',
282      'hour'=>'[01]?\d|2[0-3]',
283      'day'=>'0?[1-9]|[12]\d|3[01]',
284      'month'=>'[1-9]|1[012]',
285      'dow'=>'[0-7]'
286   );
287
288   foreach($numbers as $field=>$number) {
289     $range= "($number)(-($number)(\/\d+)?)?";
290     $field_re[$field]= "\*(\/\d+)?|$range(,$range)*";
291   }
292
293   $field_re['month'].='|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec';
294   $field_re['dow'].='|mon|tue|wed|thu|fri|sat|sun';
295
296   $fields_re= '('.join(')\s+(', $field_re).')';
297
298   /*
299   $replacements= '@reboot|@yearly|@annually|@monthly|@weekly|@daily|@midnight|@hourly';
300
301   $regexp = '^\s*('.
302                 '$'.
303                 '|#'.
304                 '|\w+\s*='.
305                 "|$fields_re\s+\S".
306                 "|($replacements)\s+\S".
307             ')';
308    */
309   // The above block from the link did not work for me.
310
311   // But this works.
312   $regexp = '/^'.$fields_re.'$/';
313         
314   if (!preg_match($regexp, $val))
315     return false;
316
317   return true;
318 }
319
320 // ttValidCondition is used to check user input to validate a notification condition.
321 function ttValidCondition($val, $emptyValid = true)
322 {
323   $val = trim($val);
324   if (strlen($val) == 0)
325     return ($emptyValid ? true : false);
326
327   // String must not be XSS evil (to insert JavaScript).
328   if (stristr($val, '<script>') || stristr($val, '<script '))
329     return false;
330
331   if (!preg_match("/^count\s?(=|[<>]=?|<>)\s?\d+$/", $val))
332     return false;
333
334   return true;
335 }
336
337 // ttValidIP is used to check user input to validate a comma-separated
338 // list of IP subnet "prefixes", for example 192.168.0 (note: no .* in the end).
339 // We keep regexp checks here simple - they are not precise.
340 // For example, IPv4-mapped IPv6 addresses will fail. This may need to be fixed.
341 function ttValidIP($val, $emptyValid = false)
342 {
343   $val = trim($val);
344   if (strlen($val) == 0 && $emptyValid)
345     return true;
346
347   $subnets = explode(',', $val);
348   foreach ($subnets as $subnet) {
349     $ipv4 = preg_match('/^\d\d?\d?(\.\d\d?\d?){0,3}\.?$/', $subnet); // Not precise check.
350     $ipv6 = preg_match('/^([0-9a-fA-F]{4})(:[0-9a-fA-F]{4}){0,7}$/', $subnet); // Not precise check.
351     if (!$ipv4 && !$ipv6)
352       return false;
353   }
354   return true;
355 }
356
357 // ttAccessAllowed checks whether user is allowed access to a particular page.
358 // It is used as an initial check on all publicly available pages
359 // (except login.php, register.php, and others where we don't have to check).
360 function ttAccessAllowed($required_right)
361 {
362   global $auth;
363   global $user;
364
365   // Redirect to login page if user is not authenticated.
366   if (!$auth->isAuthenticated()) {
367     header('Location: login.php');
368     exit();
369   }
370
371   // Check IP restriction, if set.
372   if ($user->allow_ip && !$user->can('override_allow_ip')) {
373     $access_allowed = false;
374     $user_ip = $_SERVER['REMOTE_ADDR'];
375     $allowed_ip_array = explode(',', $user->allow_ip);
376     foreach ($allowed_ip_array as $allowed_ip) {
377       $len = strlen($allowed_ip);
378       if (substr($user_ip, 0, $len) === $allowed_ip) { // startsWith check.
379          $access_allowed = true;
380          break;
381       }
382     }
383     if (!$access_allowed) return false;
384   }
385
386   // Check if user has the right.
387   if (in_array($required_right, $user->rights)) {
388     import('ttUserHelper');
389     ttUserHelper::updateLastAccess();
390     return true;
391   }
392
393   return false;
394 }
395
396 // ttStartsWith functions checks if a string starts with a given substring.
397 function ttStartsWith($string, $startString)
398 {
399     $len = strlen($startString);
400     return (substr($string, 0, $len) === $startString);
401 }
402
403 // ttEndsWith functions checks if a string ends with a given substring.
404 function ttEndsWith($string, $endString)
405 {
406     $len = strlen($endString);
407     if ($len == 0) return true;
408     return (substr($string, -$len) === $endString);
409 }
410
411 // ttDateToUserFormat converts a date from database format to user format.
412 function ttDateToUserFormat($date)
413 {
414   global $user;
415   $o_date = new DateAndTime(DB_DATEFORMAT, $date);
416   return $o_date->toString($user->date_format);
417 }
418
419 // ttRandomString generates a random alphanumeric string.
420 function ttRandomString($length = 32) {
421   $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
422   $charactersLength = strlen($characters);
423   $randomString = '';
424   for ($i = 0; $i < $length; $i++) {
425     $randomString .= $characters[rand(0, $charactersLength - 1)];
426   }
427   return $randomString;
428 }