<?php
/**
 * @package linea21.externals
 * @subpackage Absynthe-files
 * @author Sylvain 'Absynthe' Rabot <sylvain@abstraction.fr> , modified by Linea21 <info@linea21.com>
 * @link http://absynthe.is.free.fr/aFiles/
 * @version alpha 2.2
 * @access public
 * @license http://opensource.org/licenses/gpl-3.0.html
 * Absynthe files
 */


class afiles
{
  /**
   * Default path to an entity
   * @var string
   * @access private
   */
  var $path	= false;

  /**
   * Constructor which allows to chose the path of the entity used for following actions
   * @param string $path
   */
  function __construct($path = false)
  {
    $this->path = $path;
  }

  /**
   * Methods which allows to chose the path of the entity used for following actions
   * @param string $path
   */
  function set_path($path = false)
  {
    $this->path = $path;
  }

  /**
   * Return the path of the entity used by default
   * @return string
   */
  function get_path()
  {
    return $this->path;
  }

  /**
   * Return the type of an entity
   * @param string $path
   * @return string
   */
  function type($path = null)
  {
    $this->test_var($path, $this->path);

    if (is_dir($path))
    return 'dir';
    	
    else if (is_file($path))
    return 'file';

    else
    return false;
  }

  /**
   * Return the list of all entity included in the path
   * @param string $path
   * @param boolean $withroot
   * @return array
   */
  function ls($path = null, $withroot = true)
  {
    $this->test_var($path, $this->path);

    if (!is_dir($path))
    return false;
    	
    if ($handle = opendir($path))
    {
      while (false !== ($file = readdir($handle)))
      {
        if ($file != '..' && $file != '.' && $file != '')
        {
          if ($withroot)
          $infos[] = $path.'/'.$file;

          else
          $infos[] = $file;
        }
      }
      	
      closedir($handle);
      	
      $infos = array_map(array($this, 'format_path'), $infos);
      	
      return $infos;
    }
    else
    {
      return false;
    }
  }

  /**
   * Return the list of directories included in the path
   * @param string $path
   * @param boolean $withroot
   * @return array
   */
  function lsd($path = null, $withroot = true)
  {
    $this->test_var($path, $this->path);

    if (!is_dir($path))
    return false;

    $infos = array();

    if ($handle = opendir($path))
    {
      while (false !== ($file = readdir($handle)))
      {
        if (is_dir($path.'/'.$file))
        {
          if ($file != '..' && $file != '.' && $file != '')
          {
            if ($withroot)
            $infos[] = $path.'/'.$file;
            	
            else
            $infos[] = $file;
          }
        }
      }
      	
      $infos = array_map(array($this, 'format_path'), $infos);
      closedir($handle);
      	
      return $infos;
    }
    else
    {
      return false;
    }
  }

  /**
   * Return infos of all entity included in the path
   * @param string $path
   * @param boolean $withroot
   * @return array
   */
  function ll($path = null, $withroot = true)
  {
    $this->test_var($path, $this->path);

    if (!is_dir($path))
    return false;
    	
    if ($handle = opendir($path))
    {
      while (false !== ($file = readdir($handle)))
      {
        if ($file != '..' && $file != '.' && $file != '')
        {
          if ($withroot)
          {
            $temp = $this->format_path($path.'/'.$file);
            $infos[$temp] = $this->infos($temp);
          }
          else
          {
            $temp = $this->format_path($file);
            $infos[$temp] = $this->infos($path.'/'.$file);
          }
        }
      }
      	
      closedir($handle);
      	
      return $infos;
    }
    else
    {
      return false;
    }
  }

  /**
   * Return infos of all directories included in the path
   * @param string $path
   * @param boolean $withroot
   * @return array
   */
  function lld($path = null, $withroot = true)
  {
    $this->test_var($path, $this->path);

    if (!is_dir($path))
    return false;
    	
    if ($handle = opendir($path))
    {
      while (false !== ($file = readdir($handle)))
      {
        if (is_dir($path.'/'.$file))
        {
          if ($file != '..' && $file != '.' && $file != '')
          {
            if ($withroot)
            {
              $temp = $this->format_path($path.'/'.$file);
              $infos[$temp] = $this->infos($temp);
            }
            else
            {
              $temp = $this->format_path($file);
              $infos[$temp] = $this->infos($path.'/'.$file);
            }
          }
        }
      }
      	
      closedir($handle);
      	
      return $infos;
    }
    else
    {
      return false;
    }
  }

  /**
   * Create a file with or without content
   * @param string $content
   * @param string $path
   * @return boolean
   */
  function mkfile($content = '', $path = null)
  {
    $this->test_var($path, $this->path);

    if ($handle = fopen($path, 'w+'))
    {
      if (strlen($content) != 0)
      fwrite($handle, $content);

      fclose($handle);
      	
      return true;
    }
    else
    {
      return false;
    }
  }

  /**
   * Read the content of a file
   * @param string $path
   * @param boolean $byline
   * @param int $length
   * @return string/array
   */
  function read_file($path = null, $byline = false, $length = 1024)
  {
    $this->test_var($path, $this->path);

    if (!is_file($path))
    return false;

    if ($byline)
    {
      if ($handle = fopen($path, 'r'))
      {
        while (!feof($handle))
        $lines[] = fgets($handle, $length);

        fclose($handle);

        return $lines;
      }
      else
      return false;
    }
    else
    {
      return file_get_contents($path);
    }
  }

  /**
   * Create a directory with/without chmod
   * @param string $path
   * @param int $chmod
   * @return boolean
   */
  function mkdir($path = null, $chmod = null)
  {
    $this->test_var($path, $this->path);

    if (@mkdir($path))
    {
      if (!is_null($chmod))
      chmod($path, $chmod);

      return true;
    }
    else
    {
      return false;
    }
  }

  /**
   * Move a file or a directory
   * @param string $path
   * @param string $where
   * @return boolean
   */
  function mv($path, $where)
  {
    if (!is_dir($where))
    return false;

    if (is_dir($path))
    {
      $tree = $this->tree($path);
      $this->cp($path, $where);
      $this->rm($tree);
    }
    else if (is_file($path))
    {
      $this->cp($path, $where);
      $this->rm($path);
    }

    return true;
  }

  /**
   * Remove files or/and directories
   * @param string $path
   * @return boolean
   */
  function rm($path = null)
  {
    $this->test_var($path, $this->path);

    if (!is_array($path))
    $path = array($path);

    foreach ($path as $file)
    {
      if (is_dir($file))
      {
        $tree = $this->tree($file);
        rsort($tree);

        foreach ($tree as $f)
        {
          if (is_dir($f))
          rmdir($f);

          else if (is_file($f))
          unlink($f);
        }
      }
      else if (is_file($file))
      {
        unlink($file);
      }
      else
      {
        return false;
      }
    }

    return true;
  }

  /**
   * Copy files or/and directories
   * @param string $path
   * @param string $where
   * @return boolean
   */
  function cp($path, $where)
  {
    if (!is_dir($where))
    return false;
    	
    if (!is_array($path))
    {
      if (is_file($path))
      {
        $file_name = end(explode("/", $path));
        copy($path, $where.'/'.$file_name);
      }
    }
    else
    {
      foreach($path as $file)
      {
        if (is_file($file))
        {
          $file_name = end(explode("/", $file));
          copy($file, $where.'/'.$file_name);
        }

        else if (is_dir($file))
        {
          $files = $this->tree($file);
          $this->mkdir($where.'/'.$file);

          foreach ($files as $f)
          {
            if (is_file($f))
            copy($f, $where.'/'.$f);
            else if (is_dir($f))
            $this->mkdir($where.'/'.$f);
          }
        }
      }
    }

    return true;
  }

  /**
   * Return the mod of a file/directory
   * Credits goes to Ambriel_Angel (www dot ambriels dot net)
   * @param string $path
   * @return int
   */
  function mod($path)
  {
    $this->test_var($path, $this->path);

    // Initialisation
    $val	= 0;
    $perms	= fileperms($path);

    // Owner; User
    $val += (($perms & 0x0100) ? 0x0100 : 0x0000);		// Read
    $val += (($perms & 0x0080) ? 0x0080 : 0x0000);		// Write
    $val += (($perms & 0x0040) ? 0x0040 : 0x0000);		// Execute

    // Group
    $val += (($perms & 0x0020) ? 0x0020 : 0x0000);		// Read
    $val += (($perms & 0x0010) ? 0x0010 : 0x0000);		// Write
    $val += (($perms & 0x0008) ? 0x0008 : 0x0000);		// Execute

    // Global; World
    $val += (($perms & 0x0004) ? 0x0004 : 0x0000);		// Read
    $val += (($perms & 0x0002) ? 0x0002 : 0x0000);		// Write
    $val += (($perms & 0x0001) ? 0x0001 : 0x0000);		//	Execute

    // Misc
    $val += (($perms & 0x40000) ? 0x40000 : 0x0000);	// temporary file (01000000)
    $val += (($perms & 0x80000) ? 0x80000 : 0x0000); 	// compressed file (02000000)
    $val += (($perms & 0x100000) ? 0x100000 : 0x0000);	// sparse file (04000000)
    $val += (($perms & 0x0800) ? 0x0800 : 0x0000);		// Hidden file (setuid bit) (04000)
    $val += (($perms & 0x0400) ? 0x0400 : 0x0000);		// System file (setgid bit) (02000)
    $val += (($perms & 0x0200) ? 0x0200 : 0x0000);		// Archive bit (sticky bit) (01000)

    return decoct($val);
  }

  /**
   * Return infos concerning the entity
   * @param string $path
   * @param boolean $withroot
   * @param boolean $content
   * @param boolean $byline
   * @param int $length
   * @return array
   */
  function infos($path = null, $withroot = true, $content = true, $byline = false, $length = 1024)
  {
    $this->test_var($path, $this->path);

    if (is_dir($path))
    {
      if ($handle = opendir($path))
      {
        $infos['type']			= 'dir';
        $infos['path_infos']	= pathinfo($path);
        $infos['atime']			= fileatime($path);
        $infos['ctime']			= filectime($path);
        $infos['mtime']			= filemtime($path);
        $infos['chmod']			= $this->mod($path);
        $infos['owner_id']		= fileowner($path);
        $infos['owner_infos']	= posix_getpwuid($infos['owner_id']);
        $infos['group_id']		= filegroup($path);
        $infos['group_infos']	= posix_getgrgid($infos['group_id']);
        $infos['dir_count']		= 0;
        $infos['files_count']	= 0;
        $infos['size']			= $this->filesize($path);
        $infos['files']			= array();
        $infos['directories']	= array();

        while (false !== ($file = readdir($handle)))
        {
          if (is_dir($path.'/'.$file))
          {
            if ($file != '..' && $file != '.' && $file != '')
            {
              $infos['dir_count']++;
              if ($withroot)
              $infos['directories'][] = $path.'/'.$file;
              else
              $infos['directories'][] = $file;
            }
          }
          else if (is_file($path.'/'.$file))
          {
            $infos['files_count']++;

            if ($withroot)
            $infos['files'][] = $path.'/'.$file;
            else
            $infos['files'][] = $file;
          }
        }

        $infos['files']			= array_map(array($this, 'format_path'), $infos['files']);
        $infos['directories']	= array_map(array($this, 'format_path'), $infos['directories']);

        $this->sort_results($infos['directories']);
        $this->sort_results($infos['files']);

        closedir($handle);

        return $infos;
      }
      else
      {
        return false;
      }
    }
    else if (is_file($path))
    {
      if ($handle = fopen($path, 'r'))
      {
        $infos['type']			= 'file';
        $infos['path_infos']	= pathinfo($path);
        $infos['atime']			= fileatime($path);
        $infos['ctime']			= filectime($path);
        $infos['mtime']			= filemtime($path);
        $infos['chmod']			= $this->mod($path);
        $infos['owner_id']		= fileowner($path);
        $infos['owner_infos']	= posix_getpwuid($infos['owner_id']);
        $infos['group_id']		= filegroup($path);
        $infos['group_infos']	= posix_getgrgid($infos['group_id']);
        $infos['lines_count']	= 0;
        $infos['size']			= $this->filesize($path);
        $infos['md5']			= md5_file($path);
        $infos['sha1']			= sha1_file($path);

        if ($content)
        $infos['content']		= $byline === true ? array() : file_get_contents($path);

        while (!feof($handle))
        {
          if ($byline && $content)
          $infos['content'][] = fgets($handle, $length);
          else
          fgets($handle, $length);
          	
          $infos['lines_count']++;
        }

        fclose($handle);

        return $infos;
      }
      else
      {
        return false;
      }
    }
    else
    {
      return false;
    }
  }

  /**
   * Return tree of a directory
   * @param string $path
   * @param boolean $expand2files
   * @return array
   */
  function tree($path = null, $expand2files = true)
  {
    $this->test_var($path, $this->path);

    $directories = $this->lsd($path);

    for ($x = 0; $x < count($directories); $x++)
    {
      if (!is_dir($directories[$x]))
      continue;

      if ($handle = opendir($directories[$x]))
      {
        while (false !== ($file = readdir($handle)))
        {
          if (is_dir($directories[$x]."/".$file))
          {
            if ($file != '..' && $file != '.' && $file != '')
            {
              $directories[] = $directories[$x]."/".$file;
            }
          }
        }
        closedir($handle);
      }
      else
      {
        $directories[] = false;
      }
    }

    $directories[]	= $path;
    $directories	= array_map(array($this, 'format_path'), $directories);
    	
    if ($expand2files)
    {
      foreach ($directories as $dir)
      {
        $expanded_directories[] = $dir;

        if ($handle = opendir($dir))
        {
          while (false !== ($file = readdir($handle)))
          if (is_file($dir.'/'.$file))
          $expanded_directories[] = $dir.'/'.$file;
        }
        else
        {
          $expanded_directories[] = false;
        }
      }
      	
      $expanded_directories = array_map(array($this, 'format_path'), $expanded_directories);
      	
      $this->sort_results($expanded_directories);
    }
    else
    {
      $this->sort_results($directories);
    }

    return $expand2files === true ? $expanded_directories : $directories;
  }

  /**
   * Return the size of an entity
   * @param string $path
   * @return int
   */
  function filesize($path = null)
  {
    $this->test_var($path, $this->path);

    if (is_file($path))
    return filesize($path);
    else
    {
      $tree = $this->tree($path);
      $size = 0;
      	
      foreach ($tree as $file)
      if (is_file($file))
      $size += filesize($file);
      	
      return $size;
    }
  }

  /**
   * Serialize and creates a file with the serial
   * @param anything $var
   * @param string $path
   * @return boolean
   */
  function serialize($var, $path = null)
  {
    $this->test_var($path, $this->path);

    if($this->mkfile(serialize($var), $path))
    return true;
    else
    return false;
  }

  /**
   * Unserialize a file
   * @param string $path
   * @return array
   */
  function unserialize($path = null)
  {
    $this->test_var($path, $this->path);

    if(is_file($path))
    return unserialize($this->read_file($path));
    else
    return false;
  }

  /**
   * Parse a ini file
   * @param string $path
   * @return array
   */
  function parse_ini($path = null, $whithsection = true)
  {
    $this->test_var($path, $this->path);

    if(is_file($path))
    return parse_ini_file($path, $whithsection);
    else
    return false;
  }

  /**
   * Make ini file
   * @param array $content
   * @param string $path
   * @return boolean
   */
  function mkini($content, $path = null)
  {
    $this->test_var($path, $this->path);

    $out = '';

    if (!is_array($content))
    return false;

    foreach ($content as $key => $ini)
    {
      if (is_array($ini))
      {
        $out .= "\n[".$key."]\n\n";

        foreach ($ini as $var => $value)
        {
          $out .= $var." \t\t= ".$this->quote_ini($value)."\n";
        }
      }
      else
      {
        $out .= $key." \t\t= ".$this->quote_ini($ini)."\n";
      }
    }

    return $this->mkfile($out, $path);
  }

  /* Private section
   ------------------------------------------------- */

  /**
   * Set a variable to $default parameter if it's null
   * @param anything $var
   * @param anything $default
   */
  function test_var(&$var, $default)
  {
    if (is_null($var) || strlen(trim($var)) === 0)
    $var = $default;
  }

  /**
   * Replace '//' by '/' in paths
   * @param string $path
   * @return string
   */
  function format_path($path)
  {
    return preg_replace('#\/{2,}#', '/', $path);
  }

  /**
   * Quote ini var if needed
   * @param anything $var
   * @return anything
   */
  /*function quote_ini($var)
   {
   return is_string($var) === true ? '"'.str_replace('"', '\"', $var).'"' : $var;
   }
   */
  function quote_ini($var)
  {
    if(is_numeric($var) || is_bool($var)) return $var;
    else return '"'.str_replace('"', '\"', $var).'"';

  }
  /**
   * Sort results
   * @param array $array
   * @return array
   */
  function sort_results(&$array)
  {
    if (is_array($array))
    array_multisort(array_map('strtolower', $array), SORT_STRING, SORT_ASC, $array);
  }

  /**
   * Apply the callback function on files with extension "filter"
   * this method is recursive
   *
   * @param  $directory
   * @return array
   */
  function lsr($directory, $filter=false, $directory_tree)
  {
    // if the path has a slash at the end we remove it here
    if(substr($directory,-1) == '/')
    {
      $directory = substr($directory,0,-1);
    }

    // if the path is not valid or is not a directory ...
    if(!file_exists($directory) || !is_dir($directory))
    {
      // ... we return false and exit the function
      return false;

      // ... else if the path is readable
    }elseif(is_readable($directory))
    {
      // we open the directory
      $directory_list = opendir($directory);

      // and scan through the items inside
      while (false !== ($file = readdir($directory_list)))
      {
        // if the filepointer is not the current directory
        // or the parent directory
        if($file != '.' && $file != '..')
        {
          // we build the new path to scan
          $path = $directory.'/'.$file;

          // if the path is readable
          if(is_readable($path))
          {
            // we split the new path by directories
            $subdirectories = explode('/',$path);

            // if the new path is a directory
            if(is_dir($path))
            {
              // we scan the new path by calling this function
              $directory_tree=$this->lsr($path, $filter, $directory_tree);

              // if the new path is a file
            }elseif(is_file($path))
            {
              // get the file extension by taking everything after the last dot
              $extension = end(explode('.',end($subdirectories)));

              // if there is no filter set or the filter is set and matches
              if($filter === false || $filter == $extension)
              {
                array_push($directory_tree, $path);
              }
            }
          }
        }
      }
      // close the directory
      closedir($directory_list);
      // return file list
      if(isset($directory_tree))
      return $directory_tree;
      else return false;
      // if the path is not readable ...
    }else{
      // ... we return false
      return false;
    }
  }
}

?>