<?php
// $Revision: 1.1.4.1 $
// ------------------------------------------------------------------------- //
//  XooNIps - Neuroinformatics Base Platform System                          //
//  Copyright (C) 2005-2008 RIKEN, Japan All rights reserved.                //
//  http://xoonips.sourceforge.jp/                                           //
// ------------------------------------------------------------------------- //
//  This program is free software; you can redistribute it and/or modify     //
//  it under the terms of the GNU General Public License as published by     //
//  the Free Software Foundation; either version 2 of the License, or        //
//  (at your option) any later version.                                      //
//                                                                           //
//  You may not change or alter any portion of this comment or credits       //
//  of supporting developers from this source code or any supporting         //
//  source code which is considered copyrighted (c) material of the          //
//  original comment or credit authors.                                      //
//                                                                           //
//  This program 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 General Public License for more details.                             //
//                                                                           //
//  You should have received a copy of the GNU General Public License        //
//  along with this program; if not, write to the Free Software              //
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA //
// ------------------------------------------------------------------------- //
if ( ! defined( 'XOOPS_ROOT_PATH' ) ) {
  exit();
}

/**
 * directory handling class
 *
 * @package xoonips_utility
 * @copyright copyright &copy; 2008 RIKEN Japan
 */
class XooNIpsUtilityDirectory extends XooNIpsUtility {

  /**
   * flag for windows
   * @access private
   * @var bool
   */
  var $is_windows = false;

  /**
   * constractor
   *
   * @access private
   */
  function XooNIpsUtilityDirectory() {
    $this->setSingleton();
    $this->is_windows = ( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' );
  }

  /**
   * get temorary directory
   *
   * @access public
   * @return string temorary directory
   */
  function get_tempdir() {
    // try to use sys_get_temp_dir() function ( >= PHP 5.2.1 )
    if ( function_exists( 'sys_get_temp_dir' ) ) {
      $path = $this->realpath( sys_get_temp_dir() );
      if ( $path !== false && $this->_check_dir_perm( $path ) ) {
        return $path;
      }
    }
    // try to get temporary directory by environment variables
    $envs = array(
      'TMP',
      'TMPDIR',
      'TEMP',
    );
    foreach ( $envs as $env ) {
      if ( ! empty( $_ENV[$env] ) ) {
        $path = $this->realpath( $_ENV[$env] );
        if ( $path !== false && $this->_check_dir_perm( $path ) ) {
          return $path;
        }
      }
    }
    // try to use dirname of tempnam() function result
    $tempfile = @tempnam( uniqid( mt_rand(), true ), 'XooNIps' );
    if ( file_exists( $tempfile ) ) {
      @unlink( $tempfile );
      $path = $this->realpath( dirname( $tempfile ) );
      if ( $path !== false && $this->_check_dir_perm( $path ) ) {
        return $path;
      }
    }
    // give up
    $this->_fatal_error( 'failed to get temporary directory path', __LINE__ );
  }

  /**
   * get template string for mkdtemp() and mkstemp() method
   *
   * @access public
   * @param string $prefix
   * @return string created template string
   */
  function get_template( $prefix ) {
    $template = sprintf( '%s/%sXXXXXX', $this->get_tempdir(), $prefix );
    return $template;
  }

  /**
   * create a name for tempoaray file
   *
   * @access public
   * @param string $dir temporary directory
   * @param string $prefix prefix name of temporary file
   * @return string created temporary file name
   */
  function tempnam( $dir, $prefix ) {
    if ( empty( $dir ) || ! $this->_check_dir_perm( $dir ) ) {
      $dir = $this->get_tempdir();
    }
    $template = sprintf( '%s/%sXXXXXX', $dir, $prefix );
    $fp = $this->_mktemp_body( $template, true );
    if ( $fp === false ) {
      return false;
    }
    fclose( $fp );
    return $template;
  }

  /**
   * create a unique tempoaray file
   *
   * @access public
   * @param string &$template template of temporary directory
   * @return resource created temporary file resource
   */
  function mkstemp( &$template ) {
    return $this->_mktemp_body( $template, true );
  }

  /**
   * create a unique tempoaray directory
   *
   * @access public
   * @param string &$template template of temporary directory
   * @return bool false if failure
   */
  function mkdtemp( &$template ) {
    return $this->_mktemp_body( $template, false );
  }

  /**
   * make directory recursive (like 'mkdir -p' command)
   *
   * @access public
   * @param string $path directory path
   * @param int $mode creation directory mode
   * @return bool false if failure
   */
  function mkdir( $path, $mode ) {
    // clear file status caches
    clearstatcache();
    // normalize directory name
    $path = $this->_normalize_dirname( $path );
    // create parent directory if not exists
    $mydirname = dirname( $path );
    if ( ! is_dir( $mydirname ) ) {
      if ( ! $this->mkdir( $mydirname, $mode ) ) {
        // failed to create parent directory
        return false;
      }
    }
    if ( is_dir( $path ) ) {
      // already exists
      return true;
    }
    return @mkdir( $path, $mode );
  }

  /**
   * remove directory recursive (like 'rm -rf' command)
   *
   * @access public
   * @param string $path directory path
   * @return bool false if failure
   */
  function rmdir( $path ) {
    // clear file status caches
    clearstatcache();
    // normalize directory name
    $path = $this->_normalize_dirname( $path );
    if ( ! is_dir( $path ) ) {
      // not directory
      return false;
    }
    if ( is_link( $path ) ) {
      // unlink symblic link
      return @unlink( $path );
    }
    if ( $dh = opendir( $path ) ) {
      while ( ( $sf = readdir( $dh ) ) !== false ) {
        if ( $sf == '.' || $sf == '..' ) {
          continue;
        }
        $subpath = $path.'/'.$sf;
        if ( is_dir( $subpath ) ) {
          // directory
          if ( ! $this->rmdir( $subpath ) ) {
            // failed to delete sub directory
            return false;
          }
        } else {
          // file
          if ( @unlink( $subpath ) === false ) {
            // failed to remove file
            return false;
          }
        }
      }
      closedir( $dh );
    }
    return @rmdir( $path );
  }

  /**
   * get real path
   *
   * @access public
   * @return string real path
   */
  function realpath( $path ) {
    $path = realpath( $path );
    if ( $path === false ) {
      // failed to resolv real path
      return $path;
    }
    return $this->_normalize_dirname( $path );
  }

  /**
   * normalize directory path
   *  - use '/' file separator for windows
   *  - remove '/' from tail if given
   *
   * @access private
   * @param string $path
   * @return string normalized directory path
   */
  function _normalize_dirname( $path ) {
    if ( $this->is_windows ) {
      // use '/' for file separator for windows platform
      $path = str_replace( '\\', '/', $path );
    }
    // remove '/' from tail if given
    if ( substr( $path, - 1 ) == '/' ) {
      $path = substr( $path, 0, strlen( $path ) - 1 );
    }
    return $path;
  }

  /**
   * check writable directory permission
   *
   * @access private
   * @param string $path
   * @return bool false if not temporary directory
   */
  function _check_dir_perm( $path ) {
    // use real path
    $path = $this->realpath( $path );
    if ( $path === false ) {
      // path not found
      return false;
    }
    if ( ! is_dir( $path ) ) {
      // not direcotry
      return false;
    }
    if ( ! is_writable( $path ) || ! is_readable( $path ) || ( ! $this->is_windows && ! is_executable( $path ) ) ) {
      // not operatable permission
      return false;
    }
    return true;
  }

  /**
   * create temporary file or direcotry
   *
   * @param string &$path path string, last 6 char must be 'XXXXXX'.
   * @param bool $is_file true if create file, false if create directory
   * @return mixed
   *  - false: failure
   *  - resource: created file resource
   *  - true: success to create directory
   */
  function _mktemp_body( &$path, $is_file ) {
    // clear file status caches
    clearstatcache();
    // use absolete path
    $mydirname = $this->realpath( dirname( $path ) );
    if ( $mydirname === false || ! $this->_check_dir_perm( $mydirname ) ) {
      // parent directory not found or invalid permission
      return false;
    }
    $fpath = $mydirname.'/'.basename( $path );
    // get base name of path
    if ( ! preg_match( '/(.+)XXXXXX$/', $fpath, $matches ) ) {
      // doesn't supply XXXXXX suffix
      return false;
    }
    $prefix = $matches[1];

    // try to create file or directory three times
    for ( $try = 0; $try < 3; $try++ ) {
      $issue = $this->_get_random_issue();
      $fpath = $prefix.$issue;
      if ( $is_file ) {
        // file creation mode
        if ( ! file_exists( $fpath ) ) {
          // open file with O_EXCL|O_CREAT flag
          $fp = @fopen( $fpath, 'xb' );
          if ( $fp !== false ) {
            @chmod( $fpath, 0600 );
            $path = $fpath;
            return $fp;
          }
        }
      } else {
        // directory creation mode
        if ( ! is_dir( $fpath ) ) {
          if ( $this->mkdir( $fpath, 0700 ) ) {
            $path = $fpath;
            return true;
          }
        }
      }
    }
    return false;
  }

  /**
   * get random issue of 6 chars
   *
   * @access private
   * @return string random issue
   */
  function _get_random_issue() {
    static $random_state = false;
    // set random state
    if ( $random_state === false ) {
      $random_state = getmypid();
    }
    // generate random chars
    $random_state = md5( uniqid( mt_rand().$random_state, true ).session_id() );
    $random_chars = substr( $random_state, 0, 6 );
    return $random_chars;
  }

  /**
   * fatal error
   *
   * @access private
   * @param string $msg error message
   * @param int $line line number
   */
  function _fatal_error( $msg, $line ) {
    if ( XOONIPS_DEBUG_MODE ) {
      echo '<pre>';
      print_r( debug_backtrace() );
      echo '</pre>';
    }
    die( 'fatal error : '.$msg.' in '.__FILE__.' at '.$line );
  }
}

/**

// example 1: create temporary file
$dirutil =& xoonips_getutility( 'directory' );
$path = '/tmp/XooNIps_XXXXXX';
$fp = $dirutil->mkstemp( $path );
if ( $fp === false ) {
  exit( 'failed to create temp file' );
}
echo 'FILE PATH:'.$path."\n";
fwrite( $fp, "HOGEHOGE" );
...
fclose( $fp );
unlink( $path );

// exampe 2: create temporary directory
$dirutil =& xoonips_getutility( 'directory' );
$path = $dirutil->get_template( 'XooNIps_' );
if ( $dirutil->mkdtemp( $path ) ) {
  exit( 'failed to create temp directory' );
}
echo 'DIRECTORY PATH:'.$path."\n";
...
$dirutil->rmdir( $path );

// example 3: create and delete directory recursive
$dirutil =& xoonips_getutility( 'directory' );
$dirutil->mkdir( '/var/tmp/okumura1/okumura2/okumura3', 0755 );
$dirutil->rmdir( '/var/tmp/okumura1' );

*/

?>
