Merge branch '6.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / setup / Installer.class.php
... / ...
CommitLineData
1<?php
2
3namespace wcf\system\setup;
4
5use wcf\system\exception\SystemException;
6use wcf\system\io\Tar;
7use wcf\util\FileUtil;
8
9/**
10 * Extracts files and directories from a tar archive.
11 *
12 * @author Marcel Werk
13 * @copyright 2001-2019 WoltLab GmbH
14 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
15 */
16class Installer
17{
18 /**
19 * directory the files are installed into
20 * @var string
21 */
22 protected $targetDir;
23
24 /**
25 * name of the source tar archive
26 * @var string
27 */
28 protected $source;
29
30 /**
31 * folder within source that limits the installed files to those within
32 * this folder
33 * @var string
34 */
35 protected $folder;
36
37 /**
38 * file handler of the installed files
39 * @var \wcf\system\setup\IFileHandler
40 */
41 protected $fileHandler;
42
43 /**
44 * Creates a new Installer object.
45 *
46 * @param string $targetDir
47 * @param string $source
48 * @param IFileHandler $fileHandler
49 * @param string $folder
50 */
51 public function __construct($targetDir, $source, $fileHandler = null, $folder = '')
52 {
53 $this->targetDir = FileUtil::addTrailingSlash($targetDir);
54 $this->source = $source;
55 $this->folder = $folder;
56 $this->fileHandler = $fileHandler;
57 $this->install();
58 }
59
60 /**
61 * Creates the target directory if necessary.
62 */
63 protected function createTargetDir()
64 {
65 if (!@\is_dir($this->targetDir)) {
66 if (!FileUtil::makePath($this->targetDir)) {
67 throw new SystemException("Could not create dir '" . $this->targetDir . "'");
68 }
69 }
70 if (FileUtil::isApacheModule() || !\is_writable($this->targetDir)) {
71 $this->makeWriteable($this->targetDir);
72 }
73 }
74
75 /**
76 * Creates a directory in the target directory.
77 *
78 * @param string $dir
79 * @throws SystemException
80 */
81 protected function createDir($dir)
82 {
83 if (!@\is_dir($this->targetDir . $dir)) {
84 $oldumask = \umask(0);
85 if (!@\mkdir($this->targetDir . $dir, 0755, true)) {
86 throw new SystemException("Could not create dir '" . $this->targetDir . $dir . "'");
87 }
88 \umask($oldumask);
89 }
90 if (FileUtil::isApacheModule() || !\is_writable($this->targetDir . $dir)) {
91 $this->makeWriteable($this->targetDir . $dir);
92 }
93 }
94
95 /**
96 * Touches a file in the target directory.
97 *
98 * @param string $file
99 */
100 public function touchFile($file)
101 {
102 @\touch($this->targetDir . $file);
103 $this->makeWriteable($this->targetDir . $file);
104 }
105
106 /**
107 * Creates a file in the target directory.
108 *
109 * @param string $file
110 * @param int $index
111 * @param Tar $tar
112 */
113 protected function createFile($file, $index, Tar $tar)
114 {
115 $tar->extract($index, $this->targetDir . $file);
116 if (FileUtil::isApacheModule() || !\is_writable($this->targetDir . $file)) {
117 $this->makeWriteable($this->targetDir . $file);
118 }
119 }
120
121 /**
122 * Starts the extracting of the files.
123 */
124 protected function install()
125 {
126 $this->createTargetDir();
127
128 // open source archive
129 $tar = $this->getTar($this->source);
130
131 // distinct directories and files
132 $directories = [];
133 $files = [];
134 foreach ($tar->getContentList() as $index => $file) {
135 if (empty($this->folder) || \str_starts_with($file['filename'], $this->folder)) {
136 if (!empty($this->folder)) {
137 $file['filename'] = \str_replace($this->folder, '', $file['filename']);
138 }
139
140 // remove leading slash
141 $file['filename'] = FileUtil::getRealPath(FileUtil::removeLeadingSlash($file['filename']));
142 if ($file['type'] == 'folder') {
143 // remove trailing slash
144 $directories[] = FileUtil::removeTrailingSlash($file['filename']);
145 } else {
146 $files[$index] = $file['filename'];
147 }
148 }
149 }
150
151 $this->checkFiles($files);
152
153 // now create the directories
154 $errors = [];
155 foreach ($directories as $dir) {
156 try {
157 $this->createDir($dir);
158 } catch (SystemException $e) {
159 $errors[] = $e->getMessage();
160 }
161 }
162
163 // now untar all files
164 foreach ($files as $index => $file) {
165 try {
166 $this->createFile($file, $index, $tar);
167 } catch (SystemException $e) {
168 $errors[] = $e->getMessage();
169 }
170 }
171 if (!empty($errors)) {
172 throw new SystemException('error(s) during the installation of the files.', 0, \implode("<br>", $errors));
173 }
174
175 $this->logFiles($files);
176
177 // close tar
178 $tar->close();
179 }
180
181 /**
182 * Opens a new tar archive.
183 *
184 * @param string $source
185 * @return Tar
186 */
187 protected function getTar($source)
188 {
189 return new Tar($source);
190 }
191
192 /**
193 * Checks whether the given files overwriting locked existing files.
194 *
195 * @param array $files
196 */
197 protected function checkFiles(&$files)
198 {
199 if ($this->fileHandler != null && $this->fileHandler instanceof IFileHandler) {
200 $this->fileHandler->checkFiles($files);
201 }
202 }
203
204 /**
205 * Logs the given files.
206 *
207 * @param array $files
208 */
209 protected function logFiles(&$files)
210 {
211 if ($this->fileHandler != null && $this->fileHandler instanceof IFileHandler) {
212 $this->fileHandler->logFiles($files);
213 }
214 }
215
216 /**
217 * Makes a file or directory writeable.
218 *
219 * @param string $target
220 */
221 protected function makeWriteable($target)
222 {
223 FileUtil::makeWritable($target);
224 }
225}