CVE-2021-41243 (published 2021-11-26T18:15Z)

There is a Potential Zip Slip Vulnerability and OS Command Injection Vulnerability on the management system of baserCMS. Users with permissions to upload files may upload crafted zip files which may execute arbitrary commands on the host operating system. This is a vulnerability that needs to be addressed when the management system is used by an unspecified number of users. If you are eligible, please update to the new version as soon as possible.

Severity: HIGH
CVSS v2 base score: 9.0 (AV:N/AC:L/Au:S/C:C/I:C/A:C)
CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-78
cwe_name: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
description: The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
extended_description: 
		   This weakness can lead to a vulnerability in environments in which the attacker does not have direct access to the operating system, such as in web applications. Alternately, if the weakness occurs in a privileged program, it could allow the attacker to specify commands that normally would not be accessible, or to call alternate commands with privileges that the attacker does not have. The problem is exacerbated if the compromised process does not follow the principle of least privilege, because the attacker-controlled commands may run with special system privileges that increases the amount of damage.
            There are at least two subtypes of OS command injection:
               
				 The application intends to execute a single, fixed program that is under its own control. It intends to use externally-supplied inputs as arguments to that program. For example, the program might use system("nslookup [HOSTNAME]") to run nslookup and allow the user to supply a HOSTNAME, which is used as an argument. Attackers cannot prevent nslookup from executing. However, if the program does not remove command separators from the HOSTNAME argument, attackers could place the separators into the arguments, which allows them to execute their own program after nslookup has finished executing.
				 The application accepts an input that it uses to fully select which program to run, as well as which commands to use. The application simply redirects this entire command to the operating system. For example, the program might use "exec([COMMAND])" to execute the [COMMAND] that was supplied by the user. If the COMMAND is under attacker control, then the attacker can execute arbitrary commands or programs. If the command is being executed using functions like exec() and CreateProcess(), the attacker might not be able to combine multiple commands together in the same line.
               
            From a weakness standpoint, these variants represent distinct programmer errors. In the first variant, the programmer clearly intends that input from untrusted parties will be part of the arguments in the command to be executed. In the second variant, the programmer does not intend for the command to be accessible to any untrusted party, but the programmer probably has not accounted for alternate ways in which malicious attackers can provide input.
         
         

Fix information:
repo_url: https://github.com/baserproject/basercms
commit_details: repo_url: https://github.com/baserproject/basercms
author: gondoh
author_date: 2021-11-25 15:17:52+09:00
msg: Merge pull request from GHSA-7rpc-9m88-cf9w

* 固定ページプレビュー実行時に入力内容検証を追加

* SimpleZipの利用を停止しZipArchiveの利用に切り替え

* 不要コード除去

* BcZip利用時、ZipArchiveをopenしていない場合エラーログが出力される問題を改善
repository: repo_url: https://github.com/baserproject/basercms
repo_name: baserproject/basercms
description: baserCMS :  Based Website Development Project
repo_language: PHP
owner: baserproject
file_changes: filename: step2.php
change_type: ModificationType.MODIFY
diff: @@ -388,7 +388,25 @@
 				</ul>
 			<?php endif ?>
 		</div>
+		
+		<div class="panel-box bca-panel-box corner10">
+			<h3 class="bca-panel-box__title"><?php echo __d('baser', '拡張モジュール') ?></h3>
+			<ul class="section">
+				<li class='<?php if ($zipOk) echo 'check'; else echo 'failed'; ?>'>
+					<?php echo __d('baser', 'Zip') ?><br/>
+					<div class="check-result">
+						<?php if ($zipOk): ?>
+							<?php echo __d('baser', '利用可') ?>
+						<?php else: ?>
+							<?php echo __d('baser', '利用不可') ?><br/>
+							<small><?php echo __d('baser', 'テーマなどのzipダウンロードが制限されます。') ?></small>
+						<?php endif ?>
+					</div>
+				</li>
+			</ul>
+		</div>
 	</div>
+		
 
 	<form action="<?php echo $this->request->base ?>/installations/step2" method="post" id="checkenv">
 		<?php echo $this->BcForm->hidden('clicked') ?>

programming_language: PHP
filename: log.php
change_type: ModificationType.MODIFY
diff: @@ -16,11 +16,13 @@
 ?>
 
 
+<?php if ($zipEnable): ?>
 <div class="section bca-main__section">
 	<h2 class="bca-main__heading" data-bca-heading-size="lg"><?php echo __d('baser', 'ログ(エラーログ)の取得') ?></h2>
 	<p class="bca-main__text"><?php echo __d('baser', 'ログ(エラーログ)をPCにダウンロードします。') ?></p>
 	<p class="bca-main__text"><?php $this->BcBaser->link(__d('baser', 'ダウンロード'), ['download'], ['class' => 'button-small bca-btn', 'data-bca-btn-type' => 'download']) ?> </p>
 </div>
+<?php endif; ?>
 
 <div class="section bca-main__section">
 	<h2 class="bca-main__heading" data-bca-heading-size="lg"><?php echo __d('baser', 'エラーログの削除') ?></h2>

programming_language: PHP
filename: setting.php
change_type: ModificationType.MODIFY
diff: @@ -478,6 +478,16 @@ $config['BcApp'] = [
 	'systemMessageLangFromSiteSetting' => true
 ];
 
+/**
+ * Zipモジュールなし
+ */
+if (!extension_loaded('zip')) {
+	unset($config['BcApp']['adminNavigation']['Systems']['Theme']['menus']['ThemesDownload']);
+	unset($config['BcApp']['adminNavigation']['Systems']['Theme']['menus']['ThemesDownloadDefaultDataPattern']);
+	unset($config['BcApp']['adminNavigation']['Systems']['Tools']['menus']['ToolsMaintenance']);
+}
+
+
 /**
  * システム要件
  */

programming_language: PHP
filename: BcManagerComponent.php
change_type: ModificationType.MODIFY
diff: @@ -1387,6 +1387,7 @@ class BcManagerComponent extends Component
 			'phpGd' => extension_loaded('gd'),
 			'phpPdo' => extension_loaded('pdo'),
 			'phpXml' => extension_loaded('xml'),
+			'phpZip' => extension_loaded('zip'),
 			'apacheRewrite' => $rewriteInstalled,
 		];
 		$check = [
@@ -1394,6 +1395,7 @@ class BcManagerComponent extends Component
 			'gdOk' => $status['phpGd'],
 			'pdoOk' => $status['phpPdo'],
 			'xmlOk' => $status['phpXml'],
+			'zipOk' => $status['phpZip'],
 			'phpVersionOk' => version_compare(preg_replace('/[a-z-]/', '', $status['phpVersion']), Configure::read('BcRequire.phpVersion'), '>='),
 			'phpMemoryOk' => ((($status['phpMemory'] >= Configure::read('BcRequire.phpMemory')) || $status['phpMemory'] == -1) === true)
 		];

programming_language: PHP
filename: PagesController.php
change_type: ModificationType.MODIFY
diff: @@ -410,8 +410,16 @@ class PagesController extends AppController
 			// POSTパラメータのコードに含まれるscriptタグをそのままHTMLに出力するとブラウザによりXSSと判定される
 			// 一度データをセッションに退避する
 			if ($this->BcContents->preview === 'default') {
+				// 入力validation
+				$check = ['content_tmp' => $this->request->data['Page']['contents_tmp']];
+				if (!$this->Page->containsScript($check)) {
+					$this->BcMessage->setError(__d('baser', '本稿欄でスクリプトの入力は許可されていません。'));
+					$this->notFound();
+				}
+				
 				$sessionKey = __CLASS__ . '_preview_default_' . $this->request->data['Content']['entity_id'];
 				$this->request->data = $this->Content->saveTmpFiles($this->request->data, mt_rand(0, 99999999));
+				
 				$this->Session->write($sessionKey, $this->request->data);
 				$query = [];
 				if ($this->request->query) {
@@ -431,6 +439,13 @@ class PagesController extends AppController
 			}
 
 			if ($this->BcContents->preview === 'draft') {
+				// 入力validation
+				$check = ['content_tmp' => $this->request->data['Page']['contents_tmp']];
+				if (!$this->Page->containsScript($check)) {
+					$this->BcMessage->setError(__d('baser', '本稿欄でスクリプトの入力は許可されていません。'));
+					$this->notFound();
+				}
+				
 				$this->request->data = $this->Content->saveTmpFiles($this->request->data, mt_rand(0, 99999999));
 				$this->request->params['Content']['eyecatch'] = $this->request->data['Content']['eyecatch'];
 

programming_language: PHP
filename: ThemesController.php
change_type: ModificationType.MODIFY
diff: @@ -10,7 +10,7 @@
  * @license         https://basercms.net/license/index.html
  */
 
-App::uses('Simplezip', 'Vendor');
+App::uses('BcZip', 'Lib');
 
 /**
  * Class ThemesController
@@ -95,7 +95,6 @@ class ThemesController extends AppController
 
 		$name = $this->request->data['Theme']['file']['name'];
 		move_uploaded_file($this->request->data['Theme']['file']['tmp_name'], TMP . $name);
-		App::uses('BcZip', 'Lib');
 		$BcZip = new BcZip();
 		if (!$BcZip->extract(TMP . $name, BASER_THEMES)) {
 			$msg = __d('baser', 'アップロードしたZIPファイルの展開に失敗しました。');
@@ -573,11 +572,17 @@ class ThemesController extends AppController
 	 */
 	public function admin_download_default_data_pattern()
 	{
+		if (!extension_loaded('zip')) {
+			$this->notFound();
+		}
+		
+		$this->autoRender = false;
 		set_time_limit(0);
 		ini_set('memory_limit', -1);
 
 		/* コアのCSVを生成 */
 		$tmpDir = TMP . 'csv' . DS;
+		$distPath = TMP . 'default.zip';
 		$Folder = new Folder();
 		$Folder->create($tmpDir);
 		emptyFolder($tmpDir);
@@ -604,13 +609,20 @@ class ThemesController extends AppController
 		}
 		ftruncate($fp, 0);
 		fwrite($fp, implode("\n", $records));
+		
 		/* ZIPに固めてダウンロード */
-		$fileName = 'default';
-		$Simplezip = new Simplezip();
-		$Simplezip->addFolder($tmpDir);
-		$Simplezip->download($fileName);
+		$bcZip = new BcZip();
+		$bcZip->create($tmpDir, $distPath);
+		
+		header("Cache-Control: no-store");
+		header("Content-Type: application/zip");
+		header("Content-Disposition: attachment; filename=" . basename($distPath) . ";");
+		header("Content-Length: " . filesize($distPath));
+		while (ob_get_level()) { ob_end_clean(); }
+		echo readfile($distPath);
+		
 		emptyFolder($tmpDir);
-		exit();
+		unlink($distPath);
 	}
 
 	/**
@@ -671,19 +683,33 @@ class ThemesController extends AppController
 	 */
 	public function admin_download()
 	{
+		if (!extension_loaded('zip')) {
+			$this->notFound();
+		}
+		
 		$this->autoRender = false;
 		$tmpDir = TMP . 'theme' . DS;
+		$orgPath = BASER_THEMES . $this->siteConfigs['theme'] . DS;
+		$sourcePath = $tmpDir . $this->siteConfigs['theme'];
+		$distPath = $sourcePath . '.zip';
+		
 		$Folder = new Folder();
 		$Folder->create($tmpDir);
-		$path = BASER_THEMES . $this->siteConfigs['theme'] . DS;
 		$Folder->copy([
-			'from' => $path,
-			'to' => $tmpDir . $this->siteConfigs['theme'],
+			'from' => $orgPath,
+			'to' => $sourcePath,
 			'chmod' => 0777
 		]);
-		$Simplezip = new Simplezip();
-		$Simplezip->addFolder($tmpDir);
-		$Simplezip->download($this->siteConfigs['theme']);
+		$bcZip = new BcZip();
+		$bcZip->create($sourcePath, $distPath);
+		
+		header("Cache-Control: no-store");
+		header("Content-Type: application/zip");
+		header("Content-Disposition: attachment; filename=" . basename($distPath) . ";");
+		header("Content-Length: " . filesize($distPath));
+		while (ob_get_level()) { ob_end_clean(); }
+		echo readfile($distPath);
+		
 		$Folder->delete($tmpDir);
 	}
 }

programming_language: PHP
filename: ToolsController.php
change_type: ModificationType.MODIFY
diff: @@ -10,7 +10,7 @@
  * @license         https://basercms.net/license/index.html
  */
 
-App::uses('Simplezip', 'Vendor');
+App::uses('BcZip', 'Lib');
 
 /**
  * Class ToolsController
@@ -93,9 +93,13 @@ class ToolsController extends AppController
 	public function admin_maintenance($mode = '')
 	{
 		$this->_checkReferer();
+		$zipEnable = extension_loaded('zip');
+		if (!$zipEnable) {
+			$this->notFound();
+		}
+		
 		switch($mode) {
 			case 'backup':
-				set_time_limit(0);
 				$this->_backupDb($this->request->query['backup_encoding']);
 				break;
 			case 'restore':
@@ -155,10 +159,10 @@ class ToolsController extends AppController
 		if (!move_uploaded_file($data['Tool']['backup']['tmp_name'], $targetPath)) {
 			return false;
 		}
-
+		
 		/* ZIPファイルを解凍する */
-		$Simplezip = new Simplezip();
-		if (!$Simplezip->unzip($targetPath, $tmpPath)) {
+		$bcZip = new BcZip();
+		if (!$bcZip->extract($targetPath, $tmpPath)) {
 			return false;
 		}
 		@unlink($targetPath);
@@ -255,8 +259,12 @@ class ToolsController extends AppController
 	 */
 	protected function _backupDb($encoding)
 	{
-		$tmpDir = TMP . 'schemas' . DS;
+		set_time_limit(0);
+		
 		$version = str_replace(' ', '_', $this->getBaserVersion());
+		$tmpDir = TMP . 'schemas' . DS;
+		$distPath = TMP . 'baserbackup_' . $version . '_' . date('Ymd_His') . '.zip';
+		
 		$this->_resetTmpSchemaFolder();
 		clearAllCache();
 		$this->_writeBackup($tmpDir . 'core' . DS, '', $encoding);
@@ -267,13 +275,21 @@ class ToolsController extends AppController
 				$this->_writeBackup($tmpDir . 'plugin' . DS, $plugin['Plugin']['name'], $encoding);
 			}
 		}
+		
 		// ZIP圧縮して出力
-		$fileName = 'baserbackup_' . $version . '_' . date('Ymd_His');
-		$Simplezip = new Simplezip();
-		$Simplezip->addFolder($tmpDir);
-		$Simplezip->download($fileName);
+		$bcZip = new BcZip();
+		$bcZip->create($tmpDir, $distPath);
+		
+		header("Cache-Control: no-store");
+		header("Content-Type: application/zip");
+		header("Content-Disposition: attachment; filename=" . basename($distPath) . ";");
+		header("Content-Length: " . filesize($distPath));
+		while (ob_get_level()) { ob_end_clean(); }
+		echo readfile($distPath);
+		
+		unlink($distPath);
 		$this->_resetTmpSchemaFolder();
-		exit();
+		return true;
 	}
 
 	/**
@@ -312,6 +328,7 @@ class ToolsController extends AppController
 	public function admin_write_schema()
 	{
 		$path = TMP . 'schemas' . DS;
+		$distPath = TMP . 'schemas.zip';
 
 		/* 表示設定 */
 		$this->pageTitle = __d('baser', 'スキーマファイル生成');
@@ -336,10 +353,26 @@ class ToolsController extends AppController
 			return;
 		}
 
-		$Simplezip = new Simplezip();
-		$Simplezip->addFolder($path);
-		$Simplezip->download('schemas');
-		exit();
+		// ZIP圧縮して出力
+		set_time_limit(0);
+		$this->autoRender = false;
+		
+		// 不要ディレクトリの削除
+		rmdir($path . 'core');
+		rmdir($path . 'plugin');
+		
+		$bcZip = new BcZip();
+		$bcZip->create($path, $distPath);
+		
+		header("Cache-Control: no-store");
+		header("Content-Type: application/zip");
+		header("Content-Disposition: attachment; filename=" . basename($distPath) . ";");
+		header("Content-Length: " . filesize($distPath));
+		while (ob_get_level()) { ob_end_clean(); }
+		echo readfile($distPath);
+		
+		unlink($distPath);
+		return true;
 	}
 
 	/**
@@ -401,12 +434,16 @@ class ToolsController extends AppController
 	 */
 	public function admin_log($mode = '')
 	{
+		$zipEnable = extension_loaded('zip');
 		$errorLogPath = TMP . 'logs' . DS . 'error.log';
 		switch($mode) {
 			case 'download':
-				set_time_limit(0);
+				if (!$zipEnable) {
+					$this->notFound();
+				}
+				$this->autoRender = false;
 				if ($this->_downloadErrorLog()) {
-					exit();
+					return;
 				}
 				$this->BcMessage->setInfo('エラーログが存在しません。');
 				$this->redirect(['action' => 'log']);
@@ -441,6 +478,7 @@ class ToolsController extends AppController
 
 		$this->pageTitle = __d('baser', 'データメンテナンス');
 		$this->help = 'tools_log';
+		$this->set('zipEnable', $zipEnable);
 		$this->set('fileSize', $fileSize);
 	}
 
@@ -451,17 +489,27 @@ class ToolsController extends AppController
 	 */
 	protected function _downloadErrorLog()
 	{
+		set_time_limit(0);
+		
 		$tmpDir = TMP . 'logs' . DS;
+		$distPath = TMP . 'basercms_logs_' . date('Ymd_His') . '.zip';
 		$Folder = new Folder($tmpDir);
 		$files = $Folder->read(true, true, false);
 		if (count($files[0]) === 0 && count($files[1]) === 0) {
 			return false;
 		}
 		// ZIP圧縮して出力
-		$fileName = 'basercms_logs_' . date('Ymd_His');
-		$Simplezip = new Simplezip();
-		$Simplezip->addFolder($tmpDir);
-		$Simplezip->download($fileName);
+		$bcZip = new BcZip();
+		$bcZip->create($tmpDir, $distPath);
+		
+		header("Cache-Control: no-store");
+		header("Content-Type: application/zip");
+		header("Content-Disposition: attachment; filename=" . basename($distPath) . ";");
+		header("Content-Length: " . filesize($distPath));
+		while (ob_get_level()) { ob_end_clean(); }
+		echo readfile($distPath);
+		
+		unlink($distPath);
 		return true;
 	}
 

programming_language: PHP
filename: BcZip.php
change_type: ModificationType.MODIFY
diff: @@ -84,6 +84,7 @@ class BcZip
 			$archivePath = $this->Zip->getNameIndex(0);
 			$archivePathAry = explode(DS, $archivePath);
 			$this->topArchiveName = $archivePathAry[0];
+			$this->Zip->close();
 			return true;
 		} else {
 			return false;
@@ -120,16 +121,6 @@ class BcZip
 		}
 	}
 
-	/**
-	 * Destruct
-	 */
-	public function __destruct()
-	{
-		if (class_exists('ZipArchive')) {
-			$this->Zip->close();
-		}
-	}
-
 	/**
 	 * CUI 向けにパスをエスケープする
 	 *
@@ -145,4 +136,46 @@ class BcZip
 		return implode(DS, $pathAry);
 	}
 
+	/**
+	 * zip生成
+	 *
+	 * @param string $sorce 元データ
+	 * @param string $dist 出力先
+	 * @return void
+	 */
+    public function create($sorce, $dist)
+    {
+        $za = new \ZipArchive();
+        $za->open($dist, \ZIPARCHIVE::CREATE);
+        $this->zipSub($za, $sorce);
+        $za->close();
+    }
+
+	/**
+	 * 再帰的にzip生成対象ファイルを追加する
+	 *
+	 * @param ZipArchive $za
+	 * @param string $path
+	 * @param string $parentPath
+	 * @return void
+	 */
+    private function zipSub($za, $path, $parentPath = '')
+    {
+        $dh = opendir($path);
+        while (($entry = readdir($dh)) !== false) {
+            if ($entry == '.' || $entry == '..') {
+            } else {
+                $localPath = $parentPath . $entry;
+                $fullpath = $path . DS . $entry;
+                if (is_file($fullpath)) {
+                    $za->addFile($fullpath, $localPath);
+                } else if (is_dir($fullpath)) {
+                    $za->addEmptyDir($localPath);
+                    $this->zipSub($za, $fullpath, $localPath . DS);
+                }
+            }
+        }
+        closedir($dh);
+    }
+
 }

programming_language: PHP
filename: Simplezip.php
change_type: ModificationType.DELETE
diff: @@ -1,499 +0,0 @@
-<?php
-/**
- * Simplezip
- *
- * baserCMS :  Based Website Development Project <https://basercms.net>
- * Copyright (c) baserCMS Users Community <https://basercms.net/community/>
- *
- * @copyright       Copyright (c) baserCMS Users Community
- * @link            https://basercms.net baserCMS Project
- * @package         Baser.Vendor
- * @since           baserCMS v 3.0.0
- * @license         https://basercms.net/license/index.html
- */
-
-/**
- * シンプルなZIP利用クラス
- *
- * CAUTION 現在、Macで圧縮したZipファイルに対応していない
- */
-class Simplezip
-{
-
-	/**
-	 * 圧縮データ
-	 *
-	 * @var        array
-	 * @access    public
-	 */
-	var $compressedData = [];
-
-	/**
-	 * Central Directory
-	 *
-	 * @var        array
-	 * @access    public
-	 */
-	var $centralDirectory = [];
-
-	/**
-	 * End Of Central Directory Record
-	 *
-	 * @var        string
-	 * @access    public
-	 */
-	var $endOfCentralDirectory = "\x50\x4b\x05\x06\x00\x00\x00\x00";
-
-	/**
-	 * オフセット
-	 *
-	 * @var        int
-	 * @access    public
-	 */
-	var $oldOffset = 0;
-
-	/**
-	 * エントリ（解凍用）
-	 *
-	 * @var        array
-	 * @access    public
-	 */
-	var $entries = [];
-
-	/**
-	 * Get Hexd Time
-	 *
-	 * @param int $time Unix timestamp
-	 * @return    hex    the date formated as a ZIP date
-	 * @access    public
-	 */
-	function getMTime($time)
-	{
-		$mtime = ($time !== null? getdate($time) : getdate());
-		$mtime = preg_replace(
-			"/(..){1}(..){1}(..){1}(..){1}/",
-			"\\x\\4\\x\\3\\x\\2\\x\\1",
-			dechex(($mtime['year'] - 1980 << 25) |
-				($mtime['mon'] << 21) |
-				($mtime['mday'] << 16) |
-				($mtime['hours'] << 11) |
-				($mtime['minutes'] << 5) |
-				($mtime['seconds'] >> 1)));
-		eval('$mtime = "' . $mtime . '";');
-		return $mtime;
-	}
-
-	/**
-	 * フォルダを追加する
-	 *
-	 * @param string $directory
-	 * @param string $put_int 追加対象位置
-	 */
-	function addFolder($directory, $put_into = '')
-	{
-
-		$handle = opendir($directory);
-		if ($handle) {
-			while(false !== ($file = readdir($handle))) {
-				if (is_file($directory . $file)) {
-					$fp = fopen($directory . $file, 'rb');
-					$fileContents = fread($fp, filesize($directory . $file));
-					fclose($fp);
-					$this->addFile($fileContents, $put_into . $file, filemtime($directory . $file));
-				} elseif ($file != '.' and $file != '..' and is_dir($directory . $file)) {
-					$this->addFolder($directory . $file . DS, $put_into . $file . DS);
-				}
-			}
-		}
-
-		closedir($handle);
-
-	}
-
-	/**
-	 * 圧縮対象データを追加
-	 *
-	 * @param binary $data
-	 * @param string $directoryName
-	 * @param int $time
-	 * @access    public
-	 */
-	function addFile($data, $directoryName, $time = 0)
-	{
-
-		$directoryName = str_replace("\\", "/", $directoryName);
-		$hexdtime = $this->getMTime($time);
-
-		$feedArrayRow = "\x50\x4b\x03\x04";
-		$feedArrayRow .= "\x14\x00";
-		$feedArrayRow .= "\x00\x00";
-		$feedArrayRow .= "\x08\x00";
-		$feedArrayRow .= $hexdtime;
-
-		$uncompressedLength = strlen($data);
-		$compression = crc32($data);
-		$gzCompressedData = gzcompress($data);
-		$gzCompressedData = substr($gzCompressedData, 2, strlen($gzCompressedData) - 6);
-		$compressedLength = strlen($gzCompressedData);
-
-		$feedArrayRow .= pack("V", $compression);
-		$feedArrayRow .= pack("V", $compressedLength);
-		$feedArrayRow .= pack("V", $uncompressedLength);
-		$feedArrayRow .= pack("v", strlen($directoryName));
-		$feedArrayRow .= pack("v", 0);
-		$feedArrayRow .= $directoryName;
-
-		$feedArrayRow .= $gzCompressedData;
-
-		$this->compressedData[] = $feedArrayRow;
-
-		$newOffset = strlen($feedArrayRow);
-
-		$addCentralRecord = "\x50\x4b\x01\x02";
-		$addCentralRecord .= "\x00\x00";
-		$addCentralRecord .= "\x14\x00";
-		$addCentralRecord .= "\x00\x00";
-		$addCentralRecord .= "\x08\x00";
-		$addCentralRecord .= $hexdtime;
-		$addCentralRecord .= pack("V", $compression);
-		$addCentralRecord .= pack("V", $compressedLength);
-		$addCentralRecord .= pack("V", $uncompressedLength);
-		$addCentralRecord .= pack("v", strlen($directoryName));
-		$addCentralRecord .= pack("v", 0);
-		$addCentralRecord .= pack("v", 0);
-		$addCentralRecord .= pack("v", 0);
-		$addCentralRecord .= pack("v", 0);
-		$addCentralRecord .= pack("V", 32);
-		$addCentralRecord .= pack("V", $this->oldOffset);
-		$addCentralRecord .= $directoryName;
-
-		$this->oldOffset += $newOffset;
-		$this->centralDirectory[] = $addCentralRecord;
-
-	}
-
-	/**
-	 * Adds "file" to archive
-	 *
-	 * @param string   file contents
-	 * @param string   name of the file in the archive (may contains the path)
-	 * @param integer  the current timestamp
-	 */
-	function unix2DosTime($unixtime = 0)
-	{
-		$timearray = ($unixtime == 0)? getdate() : getdate($unixtime);
-
-		if ($timearray['year'] < 1980) {
-			$timearray['year'] = 1980;
-			$timearray['mon'] = 1;
-			$timearray['mday'] = 1;
-			$timearray['hours'] = 0;
-			$timearray['minutes'] = 0;
-			$timearray['seconds'] = 0;
-		} // end if
-
-		return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
-			($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
-	} // end of the 'unix2DosTime()' method
-
-	/**
-	 * addFile2
-	 *
-	 * @param mixed $data
-	 * @param string $name
-	 * @param int $time
-	 */
-	function addFile2($data, $name, $time = 0)
-	{
-		$name = str_replace('\\', '/', $name);
-
-		$dtime = dechex($this->unix2DosTime($time));
-		$hexdtime = '\x' . $dtime[6] . $dtime[7]
-			. '\x' . $dtime[4] . $dtime[5]
-			. '\x' . $dtime[2] . $dtime[3]
-			. '\x' . $dtime[0] . $dtime[1];
-		eval('$hexdtime = "' . $hexdtime . '";');
-
-		$fr = "\x50\x4b\x03\x04";
-		$fr .= "\x14\x00";            // ver needed to extract
-		$fr .= "\x00\x00";            // gen purpose bit flag
-		$fr .= "\x08\x00";            // compression method
-		$fr .= $hexdtime;             // last mod time and date
-
-		// "local file header" segment
-		$unc_len = strlen($data);
-		$crc = crc32($data);
-		$zdata = gzcompress($data);
-		$zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
-		$c_len = strlen($zdata);
-		$fr .= pack('V', $crc);             // crc32
-		$fr .= pack('V', $c_len);           // compressed filesize
-		$fr .= pack('V', $unc_len);         // uncompressed filesize
-		$fr .= pack('v', strlen($name));    // length of filename
-		$fr .= pack('v', 0);                // extra field length
-		$fr .= $name;
-
-		// "file data" segment
-		$fr .= $zdata;
-
-		// "data descriptor" segment (optional but necessary if archive is not
-		// served as file)
-		// nijel(2004-10-19): this seems not to be needed at all and causes
-		// problems in some cases (bug #1037737)
-		//$fr .= pack('V', $crc);                 // crc32
-		//$fr .= pack('V', $c_len);               // compressed filesize
-		//$fr .= pack('V', $unc_len);             // uncompressed filesize
-
-		// add this entry to array
-		$this->compressedData[] = $fr;
-
-		// now add to central directory record
-		$cdrec = "\x50\x4b\x01\x02";
-		$cdrec .= "\x00\x00";                // version made by
-		$cdrec .= "\x14\x00";                // version needed to extract
-		$cdrec .= "\x00\x00";                // gen purpose bit flag
-		$cdrec .= "\x08\x00";                // compression method
-		$cdrec .= $hexdtime;                 // last mod time & date
-		$cdrec .= pack('V', $crc);           // crc32
-		$cdrec .= pack('V', $c_len);         // compressed filesize
-		$cdrec .= pack('V', $unc_len);       // uncompressed filesize
-		$cdrec .= pack('v', strlen($name)); // length of filename
-		$cdrec .= pack('v', 0);             // extra field length
-		$cdrec .= pack('v', 0);             // file comment length
-		$cdrec .= pack('v', 0);             // disk number start
-		$cdrec .= pack('v', 0);             // internal file attributes
-		$cdrec .= pack('V', 32);            // external file attributes - 'archive' bit set
-
-		$cdrec .= pack('V', $this->oldOffset); // relative offset of local header
-		$this->oldOffset += strlen($fr);
-
-		$cdrec .= $name;
-
-		// optional extra field, file comment goes here
-		// save to central directory
-		$this->centralDirectory[] = $cdrec;
-	} // end of the 'addFile()' method
-
-	/**
-	 * 圧縮されたデータを取得する
-	 *
-	 * @return    binary    $zipedData
-	 * @access    public
-	 */
-	function getZippedData()
-	{
-
-		$data = implode("", $this->compressedData);
-		$controlDirectory = implode("", $this->centralDirectory);
-
-		return
-			$data .
-			$controlDirectory .
-			$this->endOfCentralDirectory .
-			pack("v", sizeof($this->centralDirectory)) .
-			pack("v", sizeof($this->centralDirectory)) .
-			pack("V", strlen($controlDirectory)) .
-			pack("V", strlen($data)) . "\x00\x00";
-	}
-
-	/**
-	 * 圧縮ファイルをダウンロードする
-	 *
-	 * @param string $archiveName
-	 */
-	function download($archiveName)
-	{
-
-		$headerInfo = '';
-
-		if (ini_get('zlib.output_compression')) {
-			ini_set('zlib.output_compression', 'Off');
-		}
-
-		// Security checks
-		if ($archiveName == "") {
-			echo "<html><title>Download Error</title><body><BR><B>ERROR:</B> The download file was NOT SPECIFIED.</body></html>";
-			exit;
-		}
-
-		if (!preg_match('/\.zip$/', $archiveName)) {
-			$archiveName .= '.zip';
-		}
-		$zippedData = $this->getZippedData();
-		$size = strlen(bin2hex($zippedData)) / 2;
-
-		header("Pragma: public");
-		header("Expires: 0");
-		header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
-		header("Cache-Control: private", false);
-		header("Content-Type: application/zip");
-		header("Content-Disposition: attachment; filename=" . $archiveName . ";");
-		header("Content-Transfer-Encoding: binary");
-		header("Content-Length: " . $size);
-		echo $zippedData;
-
-	}
-
-	/**
-	 * 解凍したファイルを出力する
-	 *
-	 * @param <type> $path
-	 * @return <type>
-	 */
-	function unzip($source, $tareget)
-	{
-		$tareget = preg_replace('/\/$/', '', $tareget);
-		$entries = $this->_readFile($source);
-		if (!$entries) {
-			return false;
-		}
-		$result = true;
-		foreach($entries as $entry) {
-			$folder = new Folder($tareget . DS . $entry['Path'], true, 0777);
-			$fp = @fopen($tareget . DS . $entry['Path'] . DS . $entry['Name'], 'wb');
-			if ($fp) {
-				if (!fwrite($fp, $entry['Data'])) {
-					$result = false;
-				}
-			} else {
-				$result = false;
-				continue;
-			}
-			fclose($fp);
-		}
-		return $result;
-	}
-
-	/**
-	 * ZIPファイルを読み込む
-	 *
-	 * @param string $path
-	 * @return    array
-	 * @access    protected
-	 */
-	function _readFile($path)
-	{
-		ini_set('mbstring.func_overload', '0');
-		$this->entries = [];
-
-		$oF = fopen($path, 'rb');
-		$vZ = fread($oF, filesize($path));
-		fclose($oF);
-
-		$aE = explode("\x50\x4b\x05\x06", $vZ);
-		$aP = unpack('x16/v1CL', $aE[1]);
-		$this->Comment = substr($aE[1], 18, $aP['CL']);
-		$this->Comment = strtr($this->Comment, ["\r\n" => "\n",
-			"\r" => "\n"]);
-		$aE = explode("\x50\x4b\x01\x02", $vZ);
-		$aE = explode("\x50\x4b\x03\x04", $aE[0]);
-		array_shift($aE);
-
-		foreach($aE as $vZ) {
-			$aI = [];
-			$aI['E'] = 0;
-			$aI['EM'] = '';
-			$aP = unpack('v1VN/v1GPF/v1CM/v1FT/v1FD/V1CRC/V1CS/V1UCS/v1FNL', $vZ);
-			$bE = ($aP['GPF'] & 0x0001)? TRUE : FALSE;
-			$nF = $aP['FNL'];
-
-			if ($aP['GPF'] & 0x0008) {
-				$aP1 = unpack('V1CRC/V1CS/V1UCS', substr($vZ, -12));
-				$aP['CRC'] = $aP1['CRC'];
-				$aP['CS'] = $aP1['CS'];
-				$aP['UCS'] = $aP1['UCS'];
-				$vZ = substr($vZ, 0, -12);
-			}
-
-			$aI['N'] = substr($vZ, 26, $nF);
-			$aI['N'] = str_replace('/', DS, $aI['N']);
-			if (substr($aI['N'], -1) == DS) {
-				continue;
-			}
-
-			$aI['P'] = dirname($aI['N']);
-			$aI['P'] = $aI['P'] == '.'? '' : $aI['P'];
-			$aI['N'] = basename($aI['N']);
-
-			$vZ = substr($vZ, 26 + $nF);
-
-			if (strlen($vZ) != $aP['CS']) {
-				$aI['E'] = 1;
-				$aI['EM'] = 'Compressed size is not equal with the value in header information.';
-			} else {
-				if ($bE) {
-					$aI['E'] = 5;
-					$aI['EM'] = 'File is encrypted, which is not supported from this class.';
-				} else {
-					switch($aP['CM']) {
-						case 0:
-							break;
-
-						case 8:
-							$vZ = gzinflate($vZ);
-							break;
-
-						case 12:
-							if (!extension_loaded('bz2')) {
-								if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
-									@dl('php_bz2.dll');
-								} else {
-									@dl('bz2.so');
-								}
-							}
-
-							if (extension_loaded('bz2')) {
-								$vZ = bzdecompress($vZ);
-							} else {
-								$aI['E'] = 7;
-								$aI['EM'] = "PHP BZIP2 extension not available.";
-							}
-							break;
-
-						default:
-							$aI['E'] = 6;
-							$aI['EM'] = "De-/Compression method {$aP['CM']} is not supported.";
-					}
-
-					if (!$aI['E']) {
-						if ($vZ === FALSE) {
-							$aI['E'] = 2;
-							$aI['EM'] = 'Decompression of data failed.';
-						} else {
-							if (strlen($vZ) != $aP['UCS']) {
-								$aI['E'] = 3;
-								$aI['EM'] = 'Uncompressed size is not equal with the value in header information.';
-							} else {
-								if (crc32($vZ) != $aP['CRC']) {
-									$aI['E'] = 4;
-									$aI['EM'] = 'CRC32 checksum is not equal with the value in header information.';
-								}
-							}
-						}
-					}
-				}
-			}
-
-			$aI['D'] = $vZ;
-
-			$aI['T'] = mktime(($aP['FT'] & 0xf800) >> 11,
-				($aP['FT'] & 0x07e0) >> 5,
-				($aP['FT'] & 0x001f) << 1,
-				($aP['FD'] & 0x01e0) >> 5,
-				($aP['FD'] & 0x001f),
-				(($aP['FD'] & 0xfe00) >> 9) + 1980);
-			$entry = [];
-			$entry['Data'] = $aI['D'];
-			$entry['Error'] = $aI['E'];
-			$entry['ErrorMsg'] = $aI['EM'];
-			$entry['Name'] = $aI['N'];
-			$entry['Path'] = $aI['P'];
-			$entry['Time'] = $aI['T'];
-			$this->entries[] = $entry;
-		}
-
-		return $this->entries;
-
-	}
-
-}

programming_language: unknown