1<?php 2/** 3 * @package Joomla.Platform 4 * @subpackage Archive 5 * 6 * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. 7 * @license GNU General Public License version 2 or later; see LICENSE 8 */ 9 10defined('JPATH_PLATFORM') or die; 11 12jimport('joomla.filesystem.file'); 13 14/** 15 * Gzip format adapter for the JArchive class 16 * 17 * This class is inspired from and draws heavily in code and concept from the Compress package of 18 * The Horde Project <https://www.horde.org> 19 * 20 * @contributor Michael Slusarz <slusarz@horde.org> 21 * @contributor Michael Cochrane <mike@graftonhall.co.nz> 22 * 23 * @since 1.5 24 * @deprecated 4.0 use the Joomla\Archive\Gzip class instead 25 */ 26class JArchiveGzip implements JArchiveExtractable 27{ 28 /** 29 * Gzip file flags. 30 * 31 * @var array 32 * @since 1.5 33 */ 34 private $_flags = array('FTEXT' => 0x01, 'FHCRC' => 0x02, 'FEXTRA' => 0x04, 'FNAME' => 0x08, 'FCOMMENT' => 0x10); 35 36 /** 37 * Gzip file data buffer 38 * 39 * @var string 40 * @since 1.5 41 */ 42 private $_data = null; 43 44 /** 45 * Extract a Gzip compressed file to a given path 46 * 47 * @param string $archive Path to ZIP archive to extract 48 * @param string $destination Path to extract archive to 49 * @param array $options Extraction options [unused] 50 * 51 * @return boolean True if successful 52 * 53 * @since 1.5 54 * @throws RuntimeException 55 */ 56 public function extract($archive, $destination, array $options = array()) 57 { 58 $this->_data = null; 59 60 if (!extension_loaded('zlib')) 61 { 62 return $this->raiseWarning(100, 'The zlib extension is not available.'); 63 } 64 65 if (isset($options['use_streams']) && $options['use_streams'] != false) 66 { 67 return $this->extractStream($archive, $destination, $options); 68 } 69 70 $this->_data = file_get_contents($archive); 71 72 if (!$this->_data) 73 { 74 return $this->raiseWarning(100, 'Unable to read archive'); 75 } 76 77 $position = $this->_getFilePosition(); 78 $buffer = gzinflate(substr($this->_data, $position, strlen($this->_data) - $position)); 79 80 if (empty($buffer)) 81 { 82 return $this->raiseWarning(100, 'Unable to decompress data'); 83 } 84 85 if (JFile::write($destination, $buffer) === false) 86 { 87 return $this->raiseWarning(100, 'Unable to write archive'); 88 } 89 90 return true; 91 } 92 93 /** 94 * Method to extract archive using stream objects 95 * 96 * @param string $archive Path to ZIP archive to extract 97 * @param string $destination Path to extract archive to 98 * @param array $options Extraction options [unused] 99 * 100 * @return boolean True if successful 101 * 102 * @since 3.6.0 103 */ 104 protected function extractStream($archive, $destination, $options = array()) 105 { 106 // New style! streams! 107 $input = JFactory::getStream(); 108 109 // Use gz 110 $input->set('processingmethod', 'gz'); 111 112 if (!$input->open($archive)) 113 { 114 return $this->raiseWarning(100, 'Unable to read archive (gz)'); 115 } 116 117 $output = JFactory::getStream(); 118 119 if (!$output->open($destination, 'w')) 120 { 121 $input->close(); 122 123 return $this->raiseWarning(100, 'Unable to write archive (gz)'); 124 } 125 126 do 127 { 128 $this->_data = $input->read($input->get('chunksize', 8196)); 129 130 if ($this->_data && !$output->write($this->_data)) 131 { 132 $input->close(); 133 134 return $this->raiseWarning(100, 'Unable to write file (gz)'); 135 } 136 } 137 138 while ($this->_data); 139 140 $output->close(); 141 $input->close(); 142 143 return true; 144 } 145 146 /** 147 * Temporary private method to isolate JError from the extract method 148 * This code should be removed when JError is removed. 149 * 150 * @param int $code The application-internal error code for this error 151 * @param string $msg The error message, which may also be shown the user if need be. 152 * 153 * @return JException JException instance if JError class exists 154 * 155 * @since 3.6.0 156 * @throws RuntimeException if JError class does not exist 157 */ 158 private function raiseWarning($code, $msg) 159 { 160 if (class_exists('JError')) 161 { 162 return JError::raiseWarning($code, $msg); 163 } 164 165 throw new RuntimeException($msg); 166 } 167 168 /** 169 * Tests whether this adapter can unpack files on this computer. 170 * 171 * @return boolean True if supported 172 * 173 * @since 2.5.0 174 */ 175 public static function isSupported() 176 { 177 return extension_loaded('zlib'); 178 } 179 180 /** 181 * Get file data offset for archive 182 * 183 * @return integer Data position marker for archive 184 * 185 * @since 1.5 186 * @throws RuntimeException 187 */ 188 public function _getFilePosition() 189 { 190 // Gzipped file... unpack it first 191 $position = 0; 192 $info = @ unpack('CCM/CFLG/VTime/CXFL/COS', substr($this->_data, $position + 2)); 193 194 if (!$info) 195 { 196 return $this->raiseWarning(100, 'Unable to decompress data.'); 197 } 198 199 $position += 10; 200 201 if ($info['FLG'] & $this->_flags['FEXTRA']) 202 { 203 $XLEN = unpack('vLength', substr($this->_data, $position + 0, 2)); 204 $XLEN = $XLEN['Length']; 205 $position += $XLEN + 2; 206 } 207 208 if ($info['FLG'] & $this->_flags['FNAME']) 209 { 210 $filenamePos = strpos($this->_data, "\x0", $position); 211 $position = $filenamePos + 1; 212 } 213 214 if ($info['FLG'] & $this->_flags['FCOMMENT']) 215 { 216 $commentPos = strpos($this->_data, "\x0", $position); 217 $position = $commentPos + 1; 218 } 219 220 if ($info['FLG'] & $this->_flags['FHCRC']) 221 { 222 $hcrc = unpack('vCRC', substr($this->_data, $position + 0, 2)); 223 $hcrc = $hcrc['CRC']; 224 $position += 2; 225 } 226 227 return $position; 228 } 229} 230