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
31if (!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 */
39function 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
81function 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
117function 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
140function 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
154function 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 */
175function 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 */
215function 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
227function 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
247function 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
268function 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
287if (!defined('LOCK_SH')) {
288    define('LOCK_SH', 1);
289}
290
291function 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 */
322if (!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 */
358if (!defined('FILE_USE_INCLUDE_PATH')) {
359  define('FILE_USE_INCLUDE_PATH', 1);
360}
361
362if (!defined('LOCK_EX')) {
363  define('LOCK_EX', 2);
364}
365
366if (!defined('FILE_APPEND')) {
367  define('FILE_APPEND', 8);
368}
369if (!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 */
444if (!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
478if (!defined('ENT_NOQUOTES')) {
479    define('ENT_NOQUOTES', 0);
480}
481
482if (!defined('ENT_COMPAT')) {
483    define('ENT_COMPAT', 2);
484}
485
486if (!defined('ENT_QUOTES')) {
487    define('ENT_QUOTES', 3);
488}
489
490if (!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?>