<?php
// $Revision: 1.8.4.1.2.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 //
// ------------------------------------------------------------------------- //

/**
 * 
 * One information in file is taken out and it returns it.
 * Unzip_next_entry calls it continuously, and file information in zip file is taken out continuously.
 * 
 * @param hdl: opened zip file
 * @return false: error in hdl argument, Local file header isn't found.
 * @return array(...) file information
 * - version:   version needed to extract
 * - bitflag:   general purpose bit flag
 * - compmethod:compression method
 * - mod_time:  last mod file time
 * - mod_date:  last mod file date
 * - crc32:     crc-32
 * - compsize:  compressed size
 * - uncompsize:uncompressed size
 * - filenamelen:   file name length
 * - extralen:  extra field length
 * - filename:  file name
 * - extra:     extra field
 * - offset:    relative offset of local file header
 */
function unzip_next_entry( $hdl )
{
    if( feof( $hdl ) ) return false;

    // if no central entries found
    // use local file headers
    $entry = array( );
    $entry['hdl'] = $hdl;
    $entry['offset']    = ftell( $hdl );
    $entry['signature'] = reset( $tmp = unpack("V",fread( $hdl, 4 )));
    if( (int)$entry['signature'] != 0x04034b50 ){
        fseek( $hdl, 0, SEEK_END );// move to the end of file
        return false;
    }
    fseek( $hdl, -4, SEEK_CUR );// rewind 4 bytes( len of signature )
    $header = unzip_read_local_file_header( $hdl );
    foreach( $header as $k => $v ){
        $entry[$k] = $v;
    }
    fseek( $hdl, $entry['compsize'], SEEK_CUR );
    return $entry;
}

function unzip_read_local_file_header( $hdl )
{
    if( !$hdl || feof( $hdl ) ) return false;
    
    $entry = array();
    $entry['signature'] = reset( $tmp = unpack("V",fread( $hdl, 4 )));
    if( (int)$entry['signature'] != 0x04034b50 ){
        fseek( $hdl, 0, SEEK_END );// move to the end of file
        return false;
    }
    $entry['version']     = reset( $tmp = unpack("v",fread( $hdl, 2 )));
    $entry['bitflag']     = reset( $tmp = unpack("v",fread( $hdl, 2 )));
    $entry['compmethod']  = reset( $tmp = unpack("v",fread( $hdl, 2 )));
    $entry['mod_time']    = reset( $tmp = unpack("v",fread( $hdl, 2 )));
    $entry['mod_date']    = reset( $tmp = unpack("v",fread( $hdl, 2 )));
    $entry['crc32']       = reset( $tmp = unpack("V",fread( $hdl, 4 )));
    $entry['compsize']    = reset( $tmp = unpack("V",fread( $hdl, 4 )));
    $entry['uncompsize']  = reset( $tmp = unpack("V",fread( $hdl, 4 )));
    $entry['filenamelen'] = reset( $tmp = unpack("v",fread( $hdl, 2 )));
    $entry['extralen']    = reset( $tmp = unpack("v",fread( $hdl, 2 )));
    if( $entry['filenamelen'] > 0 ) $entry['filename']    = fread( $hdl, $entry['filenamelen'] );
    if( $entry['extralen'] > 0 ) $entry['extra'] = fread( $hdl, $entry['extralen'] );

    return $entry;
}


/**
 * 
 * Contents of file specified arguments are taken out from zip files.
 * see unzip_next_entry and unzip_all_entries for detail about entry
 * 
 * @see unzip_next_entry
 * @see unzip_all_entries
 * 
 * @param entry: information of files ( unzip_next_entry )
 * @return contents of files or FALSE
 * 
 */
function unzip_read_data( $entry )
{
    //If there is key of 'offset', it seeks it from the file head only by offset.
    if( isset( $entry['offset'] ) ){
        fseek( $entry['hdl'], $entry['offset'], SEEK_SET );
    }
    //skip local file header
    unzip_read_local_file_header( $entry['hdl'] );
    //return result of development
    return gzinflate( fread( $entry['hdl'], $entry['compsize'] ) );
}

/**
 * 
 * Search contents of 'end of central directory' and returns it.
 * 
 * @param hdl: File handle
 * @return false: entry is not found.
 * @return array of end of central directory
 */
function unzip_end_of_central_directory_entry( $hdl )
{
    $signature = array();

    if( !$hdl || feof( $hdl ) ) return false;

    fseek( $hdl, -( 255 + 22 ), SEEK_END );

    array_push( $signature, fread( $hdl, 1 ) );
    array_push( $signature, fread( $hdl, 1 ) );
    array_push( $signature, fread( $hdl, 1 ) );
    array_push( $signature, fread( $hdl, 1 ) );

    while( !( $signature[0] == "\x50"
              && $signature[1] == "\x4b"
              && $signature[2] == "\x05"
              && $signature[3] == "\x06" ) && !feof( $hdl ) ){
        array_shift( $signature );
        array_push( $signature, fread( $hdl, 1 ) );
    }
    if( feof( $hdl ) ) return false;

    $entry['hdl']         = $hdl;    

    $entry['numofdisk'] = reset( $tmp = unpack("v",fread( $hdl, 2 )));//number of this disk             2 bytes

    $entry['numofdiskwithcentraldir'] = reset( $tmp = unpack("v",fread( $hdl, 2 )));//number of the disk with the
                                                             //start of the central directory  2 bytes

    $entry['entriescountondisk'] = reset( $tmp = unpack("v",fread( $hdl, 2 )));//total number of entries in the
                                                             //central directory on this disk  2 bytes

    $entry['entriescount'] = reset( $tmp = unpack("v",fread( $hdl, 2 )));//total number of entries in
                                                             //the central directory           2 bytes

    $entry['centraldirsize'] = reset( $tmp = unpack("V",fread( $hdl, 4 )));//size of the central directory   4 bytes

    $entry['offset'] = reset( $tmp = unpack("V",fread( $hdl, 4 )));//offset of start of central
                                                             //directory with respect to
                                                             //the starting disk number        4 bytes

    $entry['commentlen'] = reset( $tmp = unpack("v",fread( $hdl, 2 )));//.ZIP file comment length        2 bytes

    if( $entry['commentlen'] > 0 ) $entry['comment'] = fread( $hdl, $entry['commentlen'] );//.ZIP file comment       (variable size)

    return $entry;    
}

/**
 * 
 * Search 'central directory' information in next file pointer.
 * 
 * @param hdl: file handle
 * @return false: not found
 * @return array(...) information of 'central directory'
 * - hdl:           file handle
 * - versionmadeby: version made by
 * - version:       version needed to extract
 * - bitflag:       general purpose bit flag
 * - compmethod:    compression method
 * - mod_time:      last mod file time
 * - mod_date:      last mod file date
 * - crc32:         crc-32
 * - compsize:      compressed size
 * - uncompsize:    uncompressed size
 * - filenamelen:   file name length
 * - extralen:      extra field length
 * - commentlen:    file comment length
 * - disknum:       disk number start
 * - infileattr:    internal file attributes
 * - exfileattr:    external file attributes
 * - offset:        relative offset of local header
 */
function unzip_next_central_directory_entry( $hdl )
{
    $signature = array();
    $entry = array();
    
    if( !$hdl || feof( $hdl ) ) return false;
    
    array_push( $signature, fread( $hdl, 1 ) );
    array_push( $signature, fread( $hdl, 1 ) );
    array_push( $signature, fread( $hdl, 1 ) );
    array_push( $signature, fread( $hdl, 1 ) );

    while( !( $signature[0] == "\x50"
              && $signature[1] == "\x4b"
              && $signature[2] == "\x01"
              && $signature[3] == "\x02" ) && !feof( $hdl ) ){
        array_shift( $signature );
        array_push( $signature, fread( $hdl, 1 ) );
    }

    if( feof( $hdl ) ) return false;
    $entry['hdl']         = $hdl;    
    $entry['versionmadeby']     = reset( $tmp = unpack("v",fread( $hdl, 2 )));//version made by
    $entry['version']     = reset( $tmp = unpack("v",fread( $hdl, 2 )));//version needed to extract
    $entry['bitflag']     = reset( $tmp = unpack("v",fread( $hdl, 2 )));//general purpose bit flag
    $entry['compmethod']  = reset( $tmp = unpack("v",fread( $hdl, 2 )));//compression method
    $entry['mod_time']    = reset( $tmp = unpack("v",fread( $hdl, 2 )));//last mod file time
    $entry['mod_date']    = reset( $tmp = unpack("v",fread( $hdl, 2 )));//last mod file date
    $entry['crc32']       = reset( $tmp = unpack("V",fread( $hdl, 4 )));//crc-32
    $entry['compsize']    = reset( $tmp = unpack("V",fread( $hdl, 4 )));//compressed size
    $entry['uncompsize']  = reset( $tmp = unpack("V",fread( $hdl, 4 )));//uncompressed size
    $entry['filenamelen'] = reset( $tmp = unpack("v",fread( $hdl, 2 )));//file name length
    $entry['extralen']    = reset( $tmp = unpack("v",fread( $hdl, 2 )));//extra field length
    $entry['commentlen']  = reset( $tmp = unpack("v",fread( $hdl, 2 )));//file comment length
    $entry['disknum']     = reset( $tmp = unpack("v",fread( $hdl, 2 )));//disk number start
    $entry['infileattr']  = reset( $tmp = unpack("v",fread( $hdl, 2 )));//internal file attributes
    $entry['exfileattr']  = reset( $tmp = unpack("V",fread( $hdl, 4 )));//external file attributes
    $entry['offset']      = reset( $tmp = unpack("V",fread( $hdl, 4 )));//relative offset of local header

    //file name (variable size)
    if( $entry['filenamelen'] > 0 ) $entry['filename'] = fread( $hdl, $entry['filenamelen'] );

    //extra field (variable size)
    if( $entry['extralen'] > 0 ) $entry['extra'] = fread( $hdl, $entry['extralen'] );

    //file comment (variable size)
    if( $entry['commentlen'] > 0 ) $entry['comment'] = fread( $hdl, $entry['commentlen'] );

    return $entry;
}

function unzip_all_entries( $hdl )
{
    $entries = array();

    $entry = unzip_end_of_central_directory_entry( $hdl );
    if( $entry ){
        //found central directory 
        fseek( $hdl, $entry['offset'], SEEK_SET );
        while( $tmp = unzip_next_central_directory_entry( $hdl ) ){
            $entries[] = $tmp;
        }
    }else{
        fseek( $hdl, 0, SEEK_SET );
        //no central directories
        while( $tmp = unzip_next_entry( $hdl ) ){
            $entries[] = $tmp;
        }
    }
    return $entries;
}

/**
 * 
 * extract target file 
 * 
 * @see unzip_next_entry
 * @see unzip_all_entries
 * 
 * @param array $entry file entry of an archive
 * @param string $path output file path
 * @return bool false if failure
 * 
 */
function unzip_read_data_to_file( $entry, $path )
{
    // seek to offset
    if( isset( $entry['offset'] ) ){
        fseek( $entry['hdl'], $entry['offset'], SEEK_SET );
    }
    // skip local file header
    unzip_read_local_file_header( $entry['hdl'] );

    // extract data entry to temporary file.
    $tmpfile = tempnam( '/tmp', 'zip' );
    $tmpfp = fopen( $tmpfile, 'wb' );
    if( $tmpfp ){
        fwrite( $tmpfp, "\x1f" ); // ID1
        fwrite( $tmpfp, "\x8b" ); // ID2
        fwrite( $tmpfp, "\x08" ); // CM=8
        fwrite( $tmpfp, "\x00" ); // FLAGS(all zero)
        fwrite( $tmpfp, "\x00\x00\x00\x00" ); // MTIME(1970/1/1)
        fwrite( $tmpfp, "\x00" ); // XFL=4
        fwrite( $tmpfp, "\xff" ); // OS(unknown)
        // extract target file of zip archive to temporary file
        $unit = 16384; // data buffer
        $size = $entry['compsize']; // compressed data size
        while( !feof( $entry['hdl'] ) && $size > 0 ){
            if( false === fwrite( $tmpfp, fread( $entry['hdl'], min( $size, $unit ) ) ) ){
                return false;
            }
            $size -= $unit;
        }
        fwrite( $tmpfp, pack( "L", $entry['crc32'] ) ); // CRC32
        fwrite( $tmpfp, pack( "L", $entry['uncompsize'] ) ); // ISIZE
        fclose( $tmpfp );
        
        // read temporary file
        $result = true;
        $unit = 16384; // data buffer
        $fw = fopen( $path, 'wb' );
        $gzfp = gzopen( $tmpfile, 'rb' );
        while( !gzeof( $gzfp ) ){
            $buf = gzread( $gzfp, $entry['compsize'] );
            if ( $buf == "" || /* maybe corrupt zip file */
                 false === fwrite( $fw, $buf ) ){
                $result = false;
                break;
            }
        }
        fclose( $fw );
        fclose( $gzfp );
        unlink( $tmpfile );
        return $result;
    }else{
        return false;
    }
}

//echo "open ${argv[1]}\n";
//$hdl = fopen( $argv[1],"rb");
//print_r( unzip_all_entries($hdl) );
//fclose( $hdl );

//  echo "open ${argv[1]}\n";
//  $hdl = fopen( $argv[1],"rb");
//  $entry = unzip_end_of_central_directory_entry( $hdl );
//  if( $entry ){
//      fseek( $hdl, $entry['offset'], SEEK_SET );
//      $entry = unzip_next_central_directory_entry( $hdl );
//      print_r( $entry );
//  }
//  fclose( $hdl );


//  echo "open ${argv[1]}\n";
//  $hdl = fopen( $argv[1],"rb");
//  $entry = unzip_next_central_directory_entry( $hdl );
//  print_r( $entry );
//  fclose( $hdl );
?>
