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. $opened = false; if ( !$readOnly ) { $this->fp = @fopen( $fileName, "r+b" ); if ( $this->fp ) { $this->fileAccess = self::READ_WRITE; $opened = true; } } if ( !$opened ) { // 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 ( !$readOnly && $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; } } /** * Switch to read mode. * * @param int $pos Position to seek to; not used */ 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" ); 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( 0, SEEK_END ); // Doesn't Make sense, Seek-end should be at the end! while ( fgetc( $this->fp ) !== false ); } } /** * Returns if the file access is in append mode. * * @return bool */ public function isReadOnlyWriteOnlyStream() { return $this->fileAccess == self::READ_APPEND; } /** * Touches the specified file (sets the access and modification time). * * PHP system touch doesn't work correctly with the compress.zlib file. * * @param string $fileName * @return bool */ public static function touch( $fileName ) { return touch( self::getPureFileName( $fileName ) ); } /** * Returns if the specified file exists. * * @param string $fileName * @return bool */ public static function fileExists( $fileName ) { return file_exists( self::getPureFileName( $fileName ) ); } /** * Returns the specified file name without any filters or compression stream. * * @param string $fileName * @return string */ 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; } } /** * Seeks in the file to/by the specified position. * * Ways of seeking ($whence): * - SEEK_SET - $pos is absolute, seek to that position in the file * - SEEK_CUR - $pos is relative, seek by $pos bytes from the current position * * @throws ezcArchiveException * if trying to use SEEK_END for $whence * @param int $pos * @param int $whence * @return int If seek was successful or not */ 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"] ) { /** * Ugh, for some reason fseek starts throwing warnings for * zlib streams with SEEK_END. And there is no way to know this * upfront, so we need to use @ here. #fail. */ 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 ezcArchiveException( "SEEK_END in a non-seekable file is not supported (yet)." ); } $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; } } /** * Returns the current file access mode. * * @var int */ public function getFileAccess() { return $this->fileAccess; } /** * Returns if the file is in read-only mode. * * @var bool */ public function isReadOnly() { return $this->fileAccess == self::READ_ONLY; } /** * Returns if the file is new. * * @var bool */ public function isNew() { return $this->isNew; } /** * Returns if the file is modified. * * @var bool */ public function isModified() { return $this->isModified; } /** * Closes the file. */ public function close() { if ( is_resource( $this->fp ) ) { fclose( $this->fp ); $this->fp = null; } } } ?>