Commit | Line | Data |
---|---|---|
158bd3ca | 1 | <?php |
a9229942 | 2 | |
158bd3ca | 3 | namespace wcf\system\setup; |
a9229942 | 4 | |
158bd3ca TD |
5 | use wcf\system\exception\SystemException; |
6 | use wcf\system\io\Tar; | |
7 | use wcf\util\FileUtil; | |
158bd3ca TD |
8 | |
9 | /** | |
a17de04e | 10 | * Extracts files and directories from a tar archive. |
a9229942 TD |
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> | |
158bd3ca | 15 | */ |
a9229942 TD |
16 | class 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) { | |
77e8e48f | 135 | if (empty($this->folder) || \str_starts_with($file['filename'], $this->folder)) { |
a9229942 TD |
136 | if (!empty($this->folder)) { |
137 | $file['filename'] = \str_replace($this->folder, '', $file['filename']); | |
138 | } | |
139 | ||
140 | // remove leading slash | |
aaeda074 | 141 | $file['filename'] = FileUtil::getRealPath(FileUtil::removeLeadingSlash($file['filename'])); |
a9229942 TD |
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 | } | |
dcb3a44c | 225 | } |