isNew = true; $this->isEmpty = true; if( !self::touch( $fileName ) ) { throw new ezcBaseFilePermissionException( self::getPureFileName( $fileName ), ezcBaseFilePermissionException::WRITE ); } } else { $this->isNew = false; } // Try to open it in read and write mode. $this->fp = @fopen( $fileName, "r+b" ); if ( $this->fp ) { $this->fileAccess = self::READ_WRITE; } else { // Try to open it in read-only mode. $this->fp = @fopen( $fileName, "rb" ); $this->fileAccess = self::READ_ONLY; // Check if we opened the file. if ( !$this->fp ) { if( !self::fileExists( $fileName ) ) { throw new ezcBaseFileNotFoundException( $fileName ); } // Cannot read the file. throw new ezcBaseFilePermissionException( $fileName, ezcBaseFilePermissionException::READ ); } } $this->fileMetaData = stream_get_meta_data( $this->fp ); // Hardcode BZip2 to read-only. // For some reason we can open the file in read-write mode, but we cannot rewind the fp. Strange.. if ( $this->fileMetaData["wrapper_type"] == "BZip2" ) { $this->fileAccess = self::READ_ONLY; } // Why is it read only? if( $this->fileAccess == self::READ_ONLY ) { if( $this->fileMetaData["wrapper_type"] == "ZLIB" || $this->fileMetaData["wrapper_type"] == "BZip2" ) { // Append mode available? $b = @fopen( $fileName, "ab" ); if( $b !== false ) { // We have also a write-only mode. fclose( $b ); // The file is either read-only or write-only. $this->fileAccess = self::READ_APPEND; $this->readAppendSwitch = self::SWITCH_READ; } else { // Maybe we should write only to the archive. // Test this only, when the archive is new. if( $this->isNew ) { $b = @fopen( $fileName, "wb" ); if( $b !== false ) { // XXX Clean up. $this->fp = $b; $this->isEmpty = true; $this->fileAccess = self::WRITE_ONLY; $this->fileName = $fileName; $this->isModified = false; return true; } } } } } // Check if the archive is empty. if( fgetc( $this->fp ) === false ) { $this->isEmpty = true; } else { $this->rewind(); $this->isEmpty = false; } $this->fileName = $fileName; $this->isModified = false; } /** * Returns the file name or file path. * * @return string */ public function getFileName() { return $this->fileName; } /** * Switch to write mode. */ public function switchWriteMode() { // Switch only when we are in read (only) mode. if( $this->fileAccess == self::READ_APPEND && $this->readAppendSwitch == self::SWITCH_READ ) { fclose( $this->fp ); $this->fp = @fopen( $this->fileName, "ab" ); if ($this->fp === false ) { throw new ezcBaseFilePermissionException( self::getPureFileName( $this->fileName ), ezcBaseFilePermissionException::WRITE, "Cannot switch to write mode"); } $this->readAppendSwitch = self::SWITCH_APPEND; } } public function switchReadMode( $pos = 0) { // Switch only when we are in write (only) mode. if( $this->fileAccess == self::READ_APPEND && $this->readAppendSwitch == self::SWITCH_APPEND ) { fclose( $this->fp ); $this->fp = fopen( $this->fileName, "rb" ); //echo ("Switch read mode should seek to the end of the file "); if ($this->fp === false ) { throw new ezcBaseFilePermissionException( self::getPureFileName( $this->fileName ), ezcBaseFilePermissionException::READ, "Cannot switch back to read mode"); } $this->readAppendSwitch = self::SWITCH_READ; //$this->positionSeek( $pos ); $this->positionSeek( 0, SEEK_END ); // XXX DOESN'T Make sense, Seek-end should be at the end! //echo ("nonsense function called"); while( fgetc( $this->fp ) !== false); } } public function isReadOnlyWriteOnlyStream() { return $this->fileAccess == self::READ_APPEND; } /** * PHP system touch doesn't work correctly with the compress.zlib file. * */ public static function touch( $fileName ) { return touch( self::getPureFileName( $fileName ) ); } public static function fileExists( $fileName ) { return file_exists( self::getPureFileName( $fileName ) ); } /** * Return the file name without any filters or compression stream. */ private static function getPureFileName( $fileName ) { // TODO: Multistream goes wrong. if ( strncmp( $fileName, "compress.zlib://", 16 ) == 0 ) { return substr( $fileName, 16); } if ( strncmp( $fileName, "compress.bzip2://", 17 ) == 0 ) { return substr( $fileName, 17); } return $fileName; } /** * Rewind the current file, and the current() method will return the * data from the first block, if available. */ public function rewind() { if ( !is_null( $this->fp ) ) { $this->isValid = true; if ( !$this->fileMetaData["seekable"] ) { fclose( $this->fp ); $this->fp = fopen( $this->fileMetaData["uri"], $this->fileMetaData["mode"] ); } else { rewind( $this->fp ); } $this->next(); } else { $this->isValid = false; } } protected function positionSeek( $pos, $whence = SEEK_SET) { // Seek the end of the file in a write only file always succeeds. if( $this->fileAccess == self::WRITE_ONLY && $pos == 0 && $whence == SEEK_END ) return true; if ( $this->fileMetaData["seekable"] ) { return fseek( $this->fp, $pos, $whence ); } else { switch ( $whence ) { case SEEK_SET: $transPos = $pos; break; case SEEK_CUR: $transPos = $pos + ftell( $this->fp ); break; case SEEK_END: throw new Exception( "SEEK_END in a non-seekable file is not supported (yet)." ); /* $st = fstat( $this->fp ); $transPos = $pos + $st["size"]; echo ( "e" ); break; */ } $cur = ftell( $this->fp ); if ( $transPos < $cur ) { fclose( $this->fp ); $this->fp = fopen( $this->fileMetaData["uri"], $this->fileMetaData["mode"] ); $cur = 0; } for( $i = $cur; $i < $transPos; $i++) { $c = fgetc( $this->fp ); if ( $c === false ) return -1; } return 0; } } public function getFileAccess() { return $this->fileAccess; } public function isReadOnly() { return $this->fileAccess == self::READ_ONLY; } public function isNew() { return $this->isNew; } public function isModified() { return $this->isModified; } public function close() { if( is_resource( $this->fp ) ) { fclose( $this->fp ); $this->fp = null; } } } ?>