<?php

/**
 * File system object manipulation class
 *
 * @author Phi1 'l0rdphi1' Stier <l0rdphi1@liquenox.net>
 * @website www.l0rdphi1.com
 * @version 0.1
 */
class FileManip {

    
/*protected */var $dir_base;
    
/*public */var $errstr;

    
/**
     * Constructor
     *
     * @param string base working directory
     */
    /* public function __construct( */
    
function FileManip($dir_base)
    {
        
$this->dir_base rtrim($dir_base,'/');
    }
    
    
/**
     * Move file system object
     *
     * @param string object source
     * @param string object destination
     * @return boolean success
     */
    /*public */
function mv$s_object_src$s_object_dest '' )
    {
        
$this->cp($s_object_src,$s_object_dest);
        
$this->rm($s_object_src);
        return 
1;
    }
    
    
/**
     * Copy file system object
     *
     * @param string object source
     * @param string object destination
     * @return boolean success
     */
    /*public */
function cp$s_object_src$s_object_dest '' )
    {
        if ( 
$s_object_dest == '' )
        {
            
$s_object_dest $this->dir_base;
        }

        list(
$s_object_src,$s_object_dest) = $this->_fix_path(array($s_object_src,$s_object_dest));

        if ( 
$this->is_manipulable(array($s_object_src,$s_object_dest)) == )
        {
            return 
0;
        }

        
// if our destination dir doesn't exist, create it
        
if (!is_dir(dirname($s_object_dest)))
        {
            
$this->mkdir(dirname($s_object_dest));
        }

        if (
is_dir($s_object_src))
        {
            
$h_object_src opendir($s_object_src);
            while ( 
$s_inner_object_src readdir($h_object_src) )
            {
                if ( 
$s_inner_object_src != '.' && $s_inner_object_src != '..' )
                {
                    
$this->cp("$s_object_src/$s_inner_object_src","$s_object_dest/$s_inner_object_src");
                }
            }
            
closedir($h_object_src);

        }
        else if (
is_file($s_object_src))
        {
            
copy($s_object_src,$s_object_dest);
        }


        return 
1;
    }

    
/**
     * Obliterate file system object
     *
     * @param mixed object to be obliterated
     * @return boolean success
     */
    /*public */
function rm($m_object)
    {
        
$m_object $this->_fix_path($m_object);

        if ( ( 
$r_object $this->_unify_mixed_types($m_object) )=== )
        {
            return 
0;
        }
        unset(
$m_object);

        
// iterate over objects - check manipulability
        
if ( $this->is_manipulable($r_object) == )
        {
            return 
0;
        }

        
// iterate objects - for deletion now
        
foreach ( $r_object as $s_object )
        {
            if (
is_dir($s_object))
            {
                
$r_object_to_rm = array();

                
$h_object opendir($s_object);
                while ( 
$s_inner_object readdir($h_object) )
                {
                    if ( 
$s_inner_object != '.' && $s_inner_object != '..' )
                    {
                        
$r_object_to_rm[] = "$s_object/$s_inner_object";
                    }
                }
                
closedir($h_object);

                
$this->rm($r_object_to_rm);

                
rmdir($s_object);

            }
            else if (
is_file($s_object))
            {
                
unlink($s_object);
            }

        }

        return 
1;
    }

    
/**
     * Is file system object manipulable? That is, can we touch it?
     *
     * @param mixed file system object
     * @param boolean yes
     */
    /*public */
function is_manipulable($m_object)
    {
        if ( ( 
$r_object $this->_unify_mixed_types($m_object) )=== )
        {
            return 
0;
        }
        unset(
$m_object);

        
$this->errstr '';

        foreach ( 
$r_object as $s_object )
        {
            
$s_object $this->_fix_path($s_object);

            if (
is_dir($s_object))
            {
                
$r_object_to_check = array();

                if ( 
getmyuid() != fileowner($s_object) || !is_writable($s_object) )
                {
                    
$this->errstr .= "$s_object not manipulable.\n";
                }
                else
                {
                    
$h_object opendir($s_object);
                    while ( 
$s_inner_object readdir($h_object) )
                    {
                        if ( 
$s_inner_object != '.' && $s_inner_object != '..' )
                        {
                            
$r_object_to_check[] = "$s_object/$s_inner_object";
                        }
                    }
                    
closedir($h_object);

                    
$this->is_manipulable($r_object_to_check);

                }

            }
            else if (
is_file($s_object))
            {
                if ( 
getmyuid() != fileowner($s_object) || !is_writable($s_object) )
                {
                    
$this->errstr .= "$s_object not manipulable.\n";
                }
            }

        }

        if ( 
$this->errstr != '' )
        {
            return 
0;
        }

        return 
1;
    }

    
/**
     * Create directory.
     *
     * @param mixed file system object
     */
    /*public */
function mkdir($m_object)
    {
        
$m_object $this->_fix_path($m_object);

        if ( ( 
$r_object $this->_unify_mixed_types($m_object) )=== )
        {
            return 
0;
        }
        unset(
$m_object);

        
$this->errstr '';

        foreach ( 
$r_object as $s_object )
        {
            
$parts split('/',$s_object);

            
$path '';
            for ( 
$i 0$i count($parts); $i++ )
            {
                
$path .= $parts[$i].'/';

                if (!@
is_dir($path))
                {
                    @
mkdir($path);
                }

                
//@chmod($path);

            
}

            if ( !@
is_dir($s_object) )
            {
                
$this->error .= "Could not create $s_object";
            }

        }

        if ( 
$this->errstr != '' )
        {
            return 
0;
        }

        return 
1;
    }

    
/**
     * Unzip PK archive.
     * **Requires PHP be compiled with --with-zip**
     *
     * @param string file source
     * @param string directory destination
     * @return boolean success
     */
    /*public */
function unzip$s_object_src$s_object_dest '' )
    {
        if ( 
$s_object_dest == '' )
        {
            
$s_object_dest $this->dir_base;
        }

        list(
$s_object_src,$s_object_dest) = $this->_fix_path(array($s_object_src,$s_object_dest));

        
$this->errstr '';

        
$h_zip zip_open($s_object_src);
        while ( 
$h_zip_entry zip_read($h_zip) )
        {
            if ( 
zip_entry_filesize($h_zip_entry) > )
            {
                
$s_entry_file "$s_object_dest/".zip_entry_name($h_zip_entry);
                
$s_entry_dir dirname($s_entry_file);

                
$this->mkdir($s_entry_dir);

                if (
zip_entry_open($h_zip$h_zip_entry'r'))
                {
                    
$h_file fopen($s_entry_file,'w');
                    
fwrite($h_file,zip_entry_read($h_zip_entry,zip_entry_filesize($h_zip_entry)));
                    
fclose($h_file);
                    
zip_entry_close($h_zip_entry);
                }
                else
                {
                    
$this->errstr .= "Failed on $s_entry_file.\n";
                }
            }

        }
        
zip_close($h_zip);

        if ( 
$this->errstr != '' )
        {
            return 
0;
        }

        return 
1;
    }

    function 
unzip_list($s_object_src)
    {
        
$s_object_src $this->_fix_path($s_object_src);
        
$r_file_cache = array();

        
$h_zip zip_open($s_object_src);
        while ( 
$h_zip_entry zip_read($h_zip) )
        {
            
$r_file_cache[] = zip_entry_name($h_zip_entry);
        }
        
zip_close($h_zip);

        return 
$r_file_cache;
    }

    
/**
     * Ungzip Zlib achive.
     * **Requires PHP be compiled with --with-zlib**
     *
     * **I decided to use PEAR::Archive_Tar in place of this.**
     *
     * @param string file source
     * @param string file destination
     * @return boolean success
     */
    /*public */
function ungzip$s_object_src$s_object_dest '' )
    {
        
$s_src_dir dirname($s_object_src);
        
$s_src_file str_replace($s_src_dir,'',$s_object_src);

        if ( 
$s_object_dest == '' )
        {
            
$s_object_dest "$this->dir_base/$s_src_file";
        }

        list(
$s_object_src,$s_object_dest) = $this->_fix_path(array($s_object_src,$s_object_dest));

        
$s_gzdat join('',gzfile($s_object_src));
        
$fp fopen($s_object_dest,'w');
        
fwrite($fp$s_gzdatstrlen($s_gzdat));
        
fclose($fp);

        return 
1;
    }

    
/**
     * CHMOD
     *
     * @param string octal permission
     * @param mixed objects
     * @return boolean success
     */
    /*public */
function chmod$s_permission$m_object )
    {
        
$m_object $this->_fix_path($m_object);

        if ( ( 
$r_object $this->_unify_mixed_types($m_object) )=== )
        {
            return 
0;
        }
        
//unset($m_object);


        
$this->errstr '';

        foreach ( 
$r_object as $s_object )
        {
            if (!
chmod($s_object,$s_permission))
            {
                
$this->errstr .= "Could not CHMOD $s_permission $s_object\n";
            }

        }

        if ( 
$this->errstr != '' )
        {
            return 
0;
        }

        return 
1;
    }

    
/**
     * Search and replace in files
     *
     * @param string preg search
     * @param string preg replace
     * @param mixed file system object
     * @param boolean success
     */
    /*public */
function sr$s_preg_search$s_preg_replace$m_object )
    {
        
$m_object $this->_fix_path($m_object);

        if ( ( 
$r_object $this->_unify_mixed_types($m_object) )=== )
        {
            return 
0;
        }
        unset(
$m_object);

        if ( 
$this->is_manipulable($r_object) == )
        {
            return 
0;
        }

        foreach ( 
$r_object as $s_object )
        {
            
$s_fdat file_get_contents($s_object);

            if (
preg_match($s_preg_search,$s_fdat))
            {
                
$fp fopen($s_object,'w');
                
flock($fp,2);
                
fwrite($fp,preg_replace($s_preg_search,$s_preg_replace,$s_fdat));
                
fclose($fp);

                
//echo "Replaced <b>$s_preg_search</b> with <b>$s_preg_replace</b> in $s_object<br>";
            
}

            unset(
$s_fdat);
        }

        return 
1;
    }

    
/**
     * Prepend dir_base when appropriate. At some point in time, this function
     * could deal with .. and . embedded within file system object strings.
     * This function also processes * and ? wildcards.
     *
     * @param mixed file system object
     * @return mixed fixed file system object
     */
    /*protected */
function _fix_path($m_object)
    {
        if ( ( 
$r_object $this->_unify_mixed_types($m_object) )=== )
        {
            return 
0;
        }
        unset(
$m_object);

        
$r_matched_object = array();
        foreach ( 
$r_object as $i => $s_object )
        {
            
$bool_delete_entry FALSE;

            
// prepend dir_base to object if the object doesn't begin with / or ..
            // works with .
            // works with ''
            
if ( $s_object{0} != '/' && substr($s_object,0,2) != '..' )
            {
                
// deal with ./blah dirs..
                
if ( substr($s_object,0,2) == './' )
                {
                    
$s_object ltrim($s_object,'.');
                }

                
$s_object "$this->dir_base/$s_object";
            }


            
// split dir into parts
            
$r_dir_parts split('/',$s_object);

            foreach ( 
$r_dir_parts as $j => $s_dir_part )
            {
                
// any wildcards in this part of the dir?
                
if (preg_match("/[\*\?]/",$s_dir_part))
                {
                    
// translate matches to preg:
                    // the following line converts * and ? to .* and . respectively.
                    // note: this will convert inside quotes too, which may not be your desirable effect.
                    
$s_preg_dir_part str_replace( array("\*","\?"), array(".*","."), preg_quote($s_dir_part,'#') );

                    
// okay, time to open her up and check for matches
                    // but first, we must recompile our dir up to this point
                    
$s_dir $r_dir_parts[0];
                    for ( 
$k 1$k <= $j$k++ )
                    {
                        
$s_dir .= "/$r_dir_parts[$k]";
                    }

                    
// now, go for it..
                    
$h_dir opendir(dirname($s_dir));
                    while ( 
$s_inner_object readdir($h_dir) )
                    {
                        if ( 
preg_match("#^$s_preg_dir_part$#",$s_inner_object) && $s_inner_object != '.' && $s_inner_object != '..' )
                        {
                            
$r_matched_object[] = dirname($s_dir)."/$s_inner_object";
                        }
                    }
                    
closedir($h_dir);

                    
// mark wildcarded key for deletion
                    
$bool_delete_entry TRUE;

                    unset(
$s_dir);
                }
            }
            
            
$r_object[$i] = $s_object;

            
// are we to delete this file/dir?
            
if ($bool_delete_entry)
            {
                unset(
$r_object[$i]);
            }

        }

        if ( 
count($r_matched_object) > )
        {
            foreach ( 
$r_matched_object as $s_object )
            {
                
$r_object[] = $s_object;
            }
            unset(
$r_matched_object);
        }

        
//print_r($r_object);

        // if we only have 1 (or 0) object(s), return string
        
if ( count($r_object) < )
        {
            return 
$r_object[0];
        }

        
// otherwise array
        
return $r_object;
    }

    
/**
     * Unifies mixed types to array. Returns 0 on unknown type.
     *
     * @param mixed type
     * @param mixed unified
     */
    /*protected */
function _unify_mixed_types($m_object)
    {
        if (
is_array($m_object))
        {
            return 
$m_object;
        }
        else if (
is_string($m_object))
        {
            return array(
$m_object);
        }

        return 
0;
    }
}

?>