<?php

/*
** Fishlib - a collection of utilities for db-driven applications in PHP
** Copyright (C) 2002  LTWD, LLC DBA The Madfish Group
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

require_once('fishbase.class.php');
class 
FishFile extends FishBase
{
    var 
$name NULL;
    var 
$type NULL;
    var 
$size NULL;

    function 
FishFile()
    {
        
$args func_get_args();
        
$this->constructor($args);
    }

    function 
src_dir($dir=NULL)
    {
        static 
$src_dir NULL;
        if (!empty(
$dir))
            
$src_dir $dir;
        elseif (empty(
$src_dir))
            
$src_dir '/files/';
        return 
$src_dir;
    }

    function 
file_dir($dir=NULL)
    {
        static 
$file_dir NULL;

        
$new_dir $file_dir;

        if (!empty(
$dir))
            
$new_dir $dir;
        elseif (empty(
$file_dir))
            
$new_dir FishFile::src_dir();

        if (
$file_dir !== $new_dir)
        {
            if (!(
$realdir FishFile::makepath($new_dir,TRUE)))
            {
                
$private_error "FishFile: could not find or create path '$new_dir'";
                
user_error('Error initializing FishFile class'E_USER_ERROR);
                return 
FALSE;
            }
            
$new_dir $realdir;
            
$file_dir $new_dir;
        }

        return 
$file_dir;
    }

    function 
fullpath()
    {
        
$dir FishFile::file_dir();
        if (
$dir === FALSE)
            return 
FALSE;
        return 
$dir.DIRECTORY_SEPARATOR.$this->name;
    }

// upload file to file_dir 
    
function upload($field='newfile')
    {
        
$errormsg 'Could not upload file';
        
$frow FishUtil::array_key_value($_FILES$fieldNULL);
        if (empty(
$frow))
        {
            
$private_error "No entry found in _FILES for field '$field'";
            
$this->errors[] = $private_error;
            
$this->errors[] = $errormsg;
            
user_error($errormsgE_USER_WARNING);
            return 
FALSE;
        }
        if (
$frow['error'])
        {
            switch (
$frow['error'])
            {
                case 
UPLOAD_ERR_INI_SIZE:
                    
$private_error 'exceeded upload_max_filesize='
                        
ini_get('upload_max_filesize')
                    ;
                    
$errormsg .= ' : file too large';
                    break;
                case 
UPLOAD_ERR_FORM_SIZE:
                    
$private_error 'exceeded MAX_FILE_SIZE='
                        
FishUtil::array_key_value($_POST,'MAX_FILE_SIZE','NotDefined')
                    ;
                    
$errormsg .= ' : file too large';
                    break;
                case 
UPLOAD_ERR_PARTIAL:
                    
$private_error 'file only partially uploaded';
                    
$errormsg .= ' : file partially uploaded - you may want to try again';
                    break;
                case 
UPLOAD_ERR_PARTIAL:
                    
$private_error 'no file uploaded';
                    
$errormsg .= ' : file not uploaded - check for problems with your image file';
                    break;
                default:
                    
$private_error 'unknown error code';
            }
            
$private_error 'File upload error: '
                
$private_error
                
' frow='
                
var_export($frowTRUE)
            ;
            
$this->errors[] = $private_error;
            
$this->errors[] = $errormsg;
            
user_error($errormsgE_USER_WARNING);
            return 
FALSE;
        }
        
$this->size $frow['size'];
        
$this->type $frow['type'];
        if (empty(
$this->name))
        {
            
$this->name preg_replace(
                array(
'/[\s]/','/[^a-z0-9\.\-\_]/')
                , array(
'_','')
                , 
strtolower(basename($frow['name']))
            );
        }
        
$destination $this->fullpath();
        if (
$destination === FALSE)
        {
            
$private_error 'Could not create full path to destination';
            
$this->errors[] = $private_error;
            
$this->errors[] = $errormsg;
            
user_error($errormsgE_USER_WARNING);
            return 
FALSE;
        }
        if (!
move_uploaded_file($frow['tmp_name'], $destination))
        {
            
$private_error 'File upload error: error moving temp file: '
                
' frow='
                
var_export($frowTRUE)
                . 
' destination='
                
$destination
            
;
            
$this->errors[] = $private_error;
            
$this->errors[] = $errormsg;
            
user_error($errormsgE_USER_WARNING);
            return 
FALSE;
        }
        return 
TRUE;
    }

    function 
move_file($new_dir)
    {
        
$current $this->fullpath();
        if (
$current === FALSE)
        {
            
$this->errors[] = 'Error getting existing filesystem path';
            return 
FALSE;
        }
        
$destination FishFile::makepath($new_dir);
        if (
$destination === FALSE)
        {
            
$this->errors[] = "Error finding or creating filesystem path to '$new_dir'";
            return 
FALSE;
        }
        
$destination realpath("{$destination}/{$this->name}");
        if (
rename($current$destination) === FALSE)
        {
            
$this->errors[] = "Could not rename file from '$current' to '$destination'";
            return 
FALSE;
        }
        
$this->file_dir(dirname($destination));
        return 
$destination;
    }

    
//    notes on makepath: rough outline of logic involved
    // 
    // makepath ( path, use_include_path )
    //     if path is a directory
    //         return path
    //     if path exists
    //         return ERROR
    //     if mkdir(path) succeeds
    //         return path
    // 
    //     use HTML include logic to search for path
    //         if whole path found as-is
    //             return new path
    //     if use_include_path
    //         look through paths from include_path
    //             if whole path found as-is
    //                 return new path
    // 
    //     whole path not found anywhere - find next-best match
    //     ("next-best" == greatest number of directories in path already exist)
    //         back up one level and repeat, except don't error out if file exists, 
    //           just keep looking
    //         keep backing up until something is found or run out of levels
    //         if out of levels
    //             return ERROR
    //         try to create path under found location
    //         return results
    // 

    
function create_path($path)
    {
        if (
is_dir($path))
        {
            return 
realpath($path);
        }
        if (
file_exists($path))
        {
            return 
FALSE;
        }
        
$trypath realpath($path);
        if (
$trypath)
        {
            if (
mkdir($trypath))
            {
                return 
$trypath;
            }
        }
        return 
FALSE;
    }

    function 
makepath($path$use_include=FALSE)
    {
        
$result FishFile::create_path($path);
        if (
$result)
        {
            
// lucky on the first go
            
return realpath($result);
        }

        
$result FishFile::find_path_match($path$use_include);
        if (!
$result)
        {
            return 
FALSE;
        }
        if (!
is_array($result))
        {
            return 
FALSE;
        }
        list(
$root$base) = $result;
        
$path $root;
        foreach (
explode(DIRECTORY_SEPARATOR$base) as $b)
        {
            
$path .= DIRECTORY_SEPARATOR.$b;
            
$result FishFile::create_path($path);
            if (!
$result)
            {
                return 
FALSE;
            }
        }
        return 
realpath($path);
    }

    function 
find_path_match($path$use_include=FALSE)
    {
        static 
$sysroot NULL;
        if (
$sysroot === NULL)
        {
            
$sysroot realpath('/');
        }

        
$newpath FALSE;
        
$trypath $path;
        
$base '';
        if (!
$use_include)
        {
            
$extra_paths = array();
        }
        else
        {
            
$include_path ini_get('include_path');
            
$sep strchr($include_path';') === FALSE ':' ';';
            
$extra_paths explode($sep$include_path);
        }
        do
        {
            
$newpath FishFile::check_path($trypath$extra_paths);
            if (!
$newpath)
            {
                
$base basename($trypath).DIRECTORY_SEPARATOR.$base;
                
$trypath dirname($trypath);
                if (!
$trypath or dirname($trypath) === $trypath)
                {
                    
$trypath $sysroot;
                }
            }
        }
        while (!
$newpath && $trypath != $sysroot);
        if (!
$newpath)
        {
            return 
FALSE;
        }
        return array(
$newpath$base);
    }

    function 
check_realpath($path)
    {
        
$trypath realpath($path);
        if (
$trypath && is_dir($trypath))
        {
            return 
$trypath;
        }
        return 
FALSE;
    }

    function 
check_path($path$extra_paths=array())
    {
        
// look for the path as it is
        
if (($trypath FishFile::check_realpath($path)))
        {
            return 
$trypath;
        }

        
// look for the path like an HTML reference
        
if (substr($path01) == '/')
        {
            if (!empty(
$_SERVER['DOCUMENT_ROOT']))
            {
                if ((
$trypath=FishFile::check_realpath($_SERVER['DOCUMENT_ROOT'].'/'.$path)))
                {
                    return 
$trypath;
                }
            }
        }
        else
        {
            if ((
$trypath realpath('./'.$path)))
            {
                return 
$trypath;
            }
        }

        
// check any extra paths passed in
        
if (!is_array($extra_paths))
        {
            return 
FALSE;
        }
        foreach (
$extra_paths as $trypath)
        {
            
$trypath .= DIRECTORY_SEPARATOR.$path;
            if (
is_dir($trypath))
            {
                return 
$trypath;
            }
        }
        return 
FALSE;
    }

}
?>