1 <?php
2 /**
3  * phpfreechattools.class.php
4  *
5  * Copyright � 2006 Stephane Gully <stephane.gully@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, 51 Franklin St, Fifth Floor,
20  * Boston, MA  02110-1301  USA
21  */
22 
23 /**
24  * this file contains a toolbox with misc. usefull functions
25  *
26  * @author Stephane Gully <stephane.gully@gmail.com>
27  */
28 
29 // To be sure the directory separator is defined
30 // I don't know if this constant can be undefined or not so maybe this code is not necessary
31 if (!defined("DIRECTORY_SEPARATOR"))
32   define("DIRECTORY_SEPARATOR", "/");
33 
34 
35 /**
36  * Returns the absolute script filename
37  * takes care of php cgi configuration which do not support SCRIPT_FILENAME variable.
38  */
39 function pfc_GetScriptFilename()
40 {
41   $sf = '';
42   if(function_exists('debug_backtrace'))
43   {
44     // browse the backtrace history and take the first unknown filename as the client script
45     foreach(debug_backtrace() as $db)
46     {
47       $f = $db['file'];
48       if (!preg_match('/phpfreechat.class.php/',$f) &&
49           !preg_match('/pfcglobalconfig.class.php/',$f) &&
50           !preg_match('/pfctools.class.php/',$f) &&
51           !preg_match('/pfcinfo.class.php/',$f)
52           )
53       {
54         $sf = $f;
55         break;
56       }
57     }
58   }
59   else if (isset($_SERVER['PATH_TRANSLATED']) &&
60            file_exists($_SERVER['SCRIPT_FILENAME'])) // check for a cgi configurations
61   {
62     $sf = $_SERVER['PATH_TRANSLATED'];
63   }
64   else if (isset($_SERVER['SCRIPT_FILENAME'])&&
65            file_exists($_SERVER['SCRIPT_FILENAME'])) // for non-cgi configurations
66   {
67     $sf = $_SERVER['SCRIPT_FILENAME'];
68   }
69   else
70   {
71     echo "<pre>";
72     echo "<span style='color:red'>Error: pfc_GetScriptFilename function returns a wrong path. Please contact the pfc team (contact@phpfreechat.net) and copy/paste these data to help debugging:</span>\n";
73     print_r($_SERVER);
74     print_r(debug_backtrace());
75     echo "</pre>";
76     exit;
77   }
78   return $sf;
79 }
80 
81 function pfc_RelativePath($p1, $p2)
82 {
83   if (is_file($p1)) $p1 = dirname($p1);
84   if (is_file($p2)) $p2 = dirname($p2);
85   // using realpath function is necessary to resolve symbolic links
86   $p1 = realpath(cleanPath($p1));
87   $p2 = realpath(cleanPath($p2));
88   $res = "";
89   // echo $p1."<br>";
90   // echo $p2."<br>";
91   while( $p1 != "" &&
92          $p1 != "/" && // for unix root dir
93          !preg_match("/^[a-z]\:\\\$/i",$p1) && // for windows rootdir
94          strpos($p2, $p1) !== 0)
95   {
96     $res .= "../";
97     $p1 = dirname($p1);
98   }
99   if (isset($_SERVER["WINDIR"]) || isset($_SERVER["windir"])) {
100       $p2 = str_replace("\\","/",substr($p2, strlen($p1)+1, strlen($p2)-strlen($p1)));
101   } else {
102       if ($p1 === "/" || $p1 === "") {
103           $p2 = substr($p2, strlen($p1));
104       } else {
105           $p2 = substr($p2, strlen($p1)+1);
106       }
107   }
108   $res .= $p2;
109   // remove the last "/"
110   if (preg_match("/.*\/$/", $res)) $res = preg_replace("/(.*)\//","$1",$res);
111   // if rootpath is empty replace it by "." to avoide url starting with "/"
112   if ($res == "") $res = ".";
113   //  echo $res."<br>";
114   return $res;
115 }
116 
117 function cleanPath($path)
118 {
119   $result = array();
120   $pathA = explode(DIRECTORY_SEPARATOR, $path);
121   if (!$pathA[0])
122     $result[] = '';
123   foreach ($pathA AS $key => $dir) {
124     if ($dir == '..') {
125       if (end($result) == '..') {
126         $result[] = '..';
127       } elseif (!array_pop($result)) {
128         $result[] = '..';
129       }
130     } elseif ($dir && $dir != '.') {
131       $result[] = $dir;
132     }
133   }
134   if (!end($pathA))
135     $result[] = '';
136   return implode('/', $result);
137 }
138 
139 
140 function mkdir_r($path, $modedir = 0755)
141 {
142   // This function creates the specified directory using mkdir().  Note
143   // that the recursive feature on mkdir() is broken with PHP 5.0.4 for
144   // Windows, so I have to do the recursion myself.
145   if (!file_exists($path))
146   {
147     // The directory doesn't exist.  Recurse, passing in the parent
148     // directory so that it gets created.
149     mkdir_r(dirname($path), $modedir);
150     mkdir($path, $modedir);
151   }
152 }
153 
154 function rm_r($dir)
155 {
156   if(!$dh = @opendir($dir)) return;
157   while (($obj = readdir($dh)))
158   {
159     if($obj=='.' || $obj=='..') continue;
160     if (!@unlink($dir.'/'.$obj)) rm_r($dir.'/'.$obj);
161   }
162   closedir($dh);
163   @rmdir($dir);
164 }
165 
166 /**
167  * Copy a file, or recursively copy a folder and its contents
168  *
169  * @author      Aidan Lister <aidan@php.net>
170  * @link        http://aidanlister.com/repos/v/function.copyr.php
171  * @param       string   $source    Source path
172  * @param       string   $dest      Destination path
173  * @return      bool     Returns TRUE on success, FALSE on failure
174  */
175 function copy_r($source, $dest, $modedir = 0755, $modefile = 0664)
176 {
177   // Simple copy for a file
178   if (is_file($source)) {
179     $ret = copy($source, $dest);
180     chmod($dest, $modefile);
181     return $ret;
182   }
183 
184   // Make destination directory
185   if (!is_dir($dest)) {
186     mkdir($dest, $modedir);
187   }
188 
189   // Take the directories entries
190   $dir = dir($source);
191   $entries = array();
192   while (false !== $entry = $dir->read())
193   {
194     $entries[] = $entry;
195   }
196 
197   // Loop through the folder
198   foreach ($entries as $e)
199   {
200     // Skip pointers and subversion directories
201     if ($e == '.' || $e == '..' || $e == '.svn') continue;
202     // Deep copy directories
203     if ($dest !== $source . DIRECTORY_SEPARATOR . $e)
204       copy_r($source . DIRECTORY_SEPARATOR . $e, $dest . DIRECTORY_SEPARATOR . $e, $modedir, $modefile);
205   }
206 
207   // Clean up
208   $dir->close();
209   return true;
210 }
211 
212 /**
213  * Check the functions really exists on this server
214  */
215 function check_functions_exist( $f_list )
216 {
217   $errors = array();
218   foreach( $f_list as $func => $err )
219   {
220     if (!function_exists( $func ))
221       $errors[] = _pfc("%s doesn't exist: %s", $func, $err);
222   }
223   return $errors;
224 }
225 
226 
227 function test_writable_dir($dir, $name = "")
228 {
229   $errors = array();
230   if ($dir == "")
231     $errors[] = _pfc("%s directory must be specified", ($name!="" ? $name : $dir));
232 
233   if (is_file($dir))
234     $this->errors[] = _pfc("%s must be a directory",$dir);
235   if (!is_dir($dir))
236     mkdir_r($dir);
237   if (!is_dir($dir))
238     $errors[] = _pfc("%s can't be created",$dir);
239   if (!is_writeable($dir))
240     $errors[] = _pfc("%s is not writeable",$dir);
241   if (!is_readable($dir))
242     $errors[] = _pfc("%s is not readable",$dir);
243 
244   return $errors;
245 }
246 
247 function install_file($src_file, $dst_file)
248 {
249   $errors = array();
250 
251   $src_dir = dirname($src_file);
252   $dst_dir = dirname($dst_file);
253 
254   if (!is_file($src_file))
255     $errors[] = _pfc("%s is not a file", $src_file);
256   if (!is_readable($src_file))
257     $errors[] = _pfc("%s is not readable", $src_file);
258   if (!is_dir($src_dir))
259     $errors[] = _pfc("%s is not a directory", $src_dir);
260   if (!is_dir($dst_dir))
261     mkdir_r($dst_dir);
262 
263   copy( $src_file, $dst_file );
264 
265   return $errors;
266 }
267 
268 function install_dir($src_dir, $dst_dir)
269 {
270   $errors = array();
271 
272   if (!is_dir($src_dir))
273     $errors[] = _pfc("%s is not a directory", $src_dir);
274   if (!is_readable($src_dir))
275     $errors[] = _pfc("%s is not readable", $src_dir);
276 
277   copy_r( $src_dir, $dst_dir );
278 
279   return $errors;
280 }
281 
282 /**
283  * file_get_contents_flock
284  * define an alternative file_get_contents when this function doesn't exists on the used php version (<4.3.0)
285  */
286 
287 if (!defined('LOCK_SH')) {
288     define('LOCK_SH', 1);
289 }
290 
291 function file_get_contents_flock($filename, $incpath = false, $resource_context = null)
292 {
293     if (false === $fh = fopen($filename, 'rb', $incpath)) {
294         user_error('file_get_contents() failed to open stream: No such file or directory ['.$filename.']',
295             E_USER_WARNING);
296         return false;
297     }
298 
299     // Attempt to get a shared (read) lock
300     if (!flock($fh, LOCK_SH)) {
301       return false;
302     }
303 
304     clearstatcache();
305     if ($fsize = @filesize($filename)) {
306         $data = fread($fh, $fsize);
307     } else {
308         $data = '';
309         while (!feof($fh)) {
310             $data .= fread($fh, 8192);
311         }
312     }
313 
314     fclose($fh);
315     return $data;
316 }
317 
318 /**
319  * file_get_contents
320  * define an alternative file_get_contents when this function doesn't exists on the used php version (<4.3.0)
321  */
322 if (!function_exists('file_get_contents'))
323 {
324   function file_get_contents($filename, $incpath = false, $resource_context = null)
325   {
326     if (false === $fh = fopen($filename, 'rb', $incpath))
327     {
328       trigger_error('file_get_contents() failed to open stream: No such file or directory ['.$filename.']', E_USER_WARNING);
329       return false;
330     }
331     clearstatcache();
332     if ($fsize = filesize($filename))
333     {
334       $data = fread($fh, $fsize);
335     }
336     else
337     {
338       while (!feof($fh)) {
339         $data .= fread($fh, 8192);
340       }
341     }
342     fclose($fh);
343     return $data;
344   }
345 }
346 
347 /**
348  * Replace file_put_contents()
349  *
350  * @category    PHP
351  * @package     PHP_Compat
352  * @link        http://php.net/function.file_put_contents
353  * @author      Aidan Lister <aidan@php.net>
354  * @internal    resource_context is not supported
355  * @since       PHP 5
356  * @require     PHP 4.0.0 (user_error)
357  */
358 if (!defined('FILE_USE_INCLUDE_PATH')) {
359   define('FILE_USE_INCLUDE_PATH', 1);
360 }
361 
362 if (!defined('LOCK_EX')) {
363   define('LOCK_EX', 2);
364 }
365 
366 if (!defined('FILE_APPEND')) {
367   define('FILE_APPEND', 8);
368 }
369 if (!function_exists('file_put_contents')) {
370   function file_put_contents($filename, $content, $flags = null, $resource_context = null)
371     {
372       // If $content is an array, convert it to a string
373       if (is_array($content)) {
374         $content = implode('', $content);
375       }
376 
377       // If we don't have a string, throw an error
378       if (!is_scalar($content)) {
379         user_error('file_put_contents() The 2nd parameter should be either a string or an array ['.$filename.']',
380                    E_USER_WARNING);
381         return false;
382       }
383 
384       // Get the length of data to write
385       $length = strlen($content);
386 
387       // Check what mode we are using
388       $mode = ($flags & FILE_APPEND) ?
389         'a' :
390         'wb';
391 
392       // Check if we're using the include path
393       $use_inc_path = ($flags & FILE_USE_INCLUDE_PATH) ?
394         true :
395         false;
396 
397       // Open the file for writing
398       if (($fh = @fopen($filename, $mode, $use_inc_path)) === false) {
399         user_error('file_put_contents() failed to open stream: Permission denied ['.$filename.']',
400                    E_USER_WARNING);
401         return false;
402       }
403 
404       // Attempt to get an exclusive lock
405       $use_lock = ($flags & LOCK_EX) ? true : false ;
406       if ($use_lock === true) {
407         if (!flock($fh, LOCK_EX)) {
408           return false;
409         }
410       }
411 
412       // Write to the file
413       $bytes = 0;
414       if (($bytes = @fwrite($fh, $content)) === false) {
415         $errormsg = sprintf('file_put_contents() Failed to write %d bytes to %s ['.$filename.']',
416                             $length,
417                             $filename);
418         user_error($errormsg, E_USER_WARNING);
419         return false;
420       }
421 
422       // Close the handle
423       @fclose($fh);
424 
425       // Check all the data was written
426       if ($bytes != $length) {
427         $errormsg = sprintf('file_put_contents() Only %d of %d bytes written, possibly out of free disk space. ['.$filename.']',
428                             $bytes,
429                             $length);
430         user_error($errormsg, E_USER_WARNING);
431         return false;
432       }
433 
434       // Return length
435       return $bytes;
436     }
437 }
438 
439 
440 /**
441  * iconv
442  * define an alternative iconv when this function doesn't exists on the php modules
443  */
444 if (!function_exists('iconv'))
445 {
446   if(function_exists('libiconv'))
447   {
448     // use libiconv if it exists
449     function iconv($input_encoding, $output_encoding, $string)
450     {
451       return libiconv($input_encoding, $output_encoding, $string);
452     }
453   }
454   else
455   {
456     // fallback if nothing has been found
457     function iconv($input_encoding, $output_encoding, $string)
458     {
459       return $string;
460     }
461   }
462 }
463 
464 /**
465  * Replace html_entity_decode()
466  *
467  * @category    PHP
468  * @package     PHP_Compat
469  * @link        http://php.net/function.html_entity_decode
470  * @author      David Irvine <dave@codexweb.co.za>
471  * @author      Aidan Lister <aidan@php.net>
472  * @version     $Revision: 1.8 $
473  * @since       PHP 4.3.0
474  * @internal    Setting the charset will not do anything
475  * @require     PHP 4.0.0 (user_error)
476  */
477 
478 if (!defined('ENT_NOQUOTES')) {
479     define('ENT_NOQUOTES', 0);
480 }
481 
482 if (!defined('ENT_COMPAT')) {
483     define('ENT_COMPAT', 2);
484 }
485 
486 if (!defined('ENT_QUOTES')) {
487     define('ENT_QUOTES', 3);
488 }
489 
490 if (!function_exists('html_entity_decode')) {
491   function html_entity_decode($string, $quote_style = ENT_COMPAT, $charset = null)
492   {
493     if (!is_int($quote_style)) {
494       user_error('html_entity_decode() expects parameter 2 to be long, ' .
495         gettype($quote_style) . ' given', E_USER_WARNING);
496       return;
497     }
498 
499     $trans_tbl = get_html_translation_table(HTML_ENTITIES);
500     $trans_tbl = array_flip($trans_tbl);
501 
502     // Add single quote to translation table;
503     $trans_tbl['&#039;'] = '\'';
504 
505     // Not translating double quotes
506     if ($quote_style & ENT_NOQUOTES) {
507       // Remove double quote from translation table
508       unset($trans_tbl['&quot;']);
509     }
510 
511     return strtr($string, $trans_tbl);
512   }
513 }
514 
515 ?>