[TASK] Do not store files directly into typo3temp
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Classes / Utility / Repository / Helper.php
1 <?php
2 namespace TYPO3\CMS\Extensionmanager\Utility\Repository;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
18 use TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility;
19
20 /**
21 * Central utility class for repository handling.
22 */
23 class Helper implements \TYPO3\CMS\Core\SingletonInterface {
24
25 /**
26 * ##########################################
27 * Problem constants - to be used in bitmasks
28 * ##########################################
29 */
30 /**
31 * Type of problem: extension file not existing in file system.
32 *
33 * @var int
34 */
35 const PROBLEM_EXTENSION_FILE_NOT_EXISTING = 1;
36 /**
37 * Type of problem: wrong hash indicates outdated extension file.
38 *
39 * @var int
40 */
41 const PROBLEM_EXTENSION_HASH_CHANGED = 2;
42 /**
43 * Type of problem: no version records in database.
44 *
45 * @var int
46 */
47 const PROBLEM_NO_VERSIONS_IN_DATABASE = 4;
48 /**
49 * Keeps instance of repository class.
50 *
51 * @var \TYPO3\CMS\Extensionmanager\Domain\Model\Repository
52 */
53 protected $repository = NULL;
54
55 /**
56 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository
57 */
58 protected $repositoryRepository;
59
60 /**
61 * @var \TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository
62 */
63 protected $extensionRepository;
64
65 /**
66 * @var \TYPO3\CMS\Extensionmanager\Utility\ConfigurationUtility
67 */
68 protected $configurationUtility;
69
70 /**
71 * Class constructor.
72 *
73 * @access public
74 */
75 public function __construct() {
76 /** @var $objectManager \TYPO3\CMS\Extbase\Object\ObjectManager */
77 $this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
78 /** @var \TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository $repositoryRepository */
79 $repositoryRepository = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Domain\Repository\RepositoryRepository::class);
80 $this->extensionRepository = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Domain\Repository\ExtensionRepository::class);
81 $this->configurationUtility = $this->objectManager->get(ConfigurationUtility::class);
82 /** @var \TYPO3\CMS\Extensionmanager\Domain\Model\Repository $repository */
83 $repository = $repositoryRepository->findByUid(1);
84 if (is_object($repository)) {
85 $this->setRepository($repository);
86 }
87 }
88
89 /**
90 * Method registers required repository instance to work with.
91 *
92 * Repository instance is passed by reference.
93 *
94 * @access public
95 * @param \TYPO3\CMS\Extensionmanager\Domain\Model\Repository $repository
96 * @return void
97 * @see $repository
98 */
99 public function setRepository(\TYPO3\CMS\Extensionmanager\Domain\Model\Repository $repository) {
100 $this->repository = $repository;
101 }
102
103 /**
104 * Method fetches extension list file from remote server.
105 *
106 * Delegates to {@link fetchFile()}.
107 *
108 * @access public
109 * @return void
110 * @throws ExtensionManagerException
111 * @see fetchFile()
112 */
113 public function fetchExtListFile() {
114 $this->fetchFile($this->getRemoteExtListFile(), $this->getLocalExtListFile());
115 }
116
117 /**
118 * Method fetches mirror list file from remote server.
119 *
120 * Delegates to {@link fetchFile()}.
121 *
122 * @access public
123 * @return void
124 * @throws ExtensionManagerException
125 * @see fetchFile()
126 */
127 public function fetchMirrorListFile() {
128 $this->fetchFile($this->getRemoteMirrorListFile(), $this->getLocalMirrorListFile());
129 }
130
131 /**
132 * Method fetches contents from remote server and
133 * writes them into a file in the local file system.
134 *
135 * @param string $remoteResource remote resource to read contents from
136 * @param string $localResource local resource (absolute file path) to store retrieved contents to (must be within typo3temp/)
137 * @return void
138 * @see \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl(), \TYPO3\CMS\Core\Utility\GeneralUtility::writeFile()
139 * @throws ExtensionManagerException
140 */
141 protected function fetchFile($remoteResource, $localResource) {
142 if ($this->configurationUtility->getCurrentConfiguration('extensionmanager')['offlineMode']['value']) {
143 throw new ExtensionManagerException('Extension Manager is in offline mode. No TER connection available.', 1437078780);
144 }
145 if (is_string($remoteResource) && is_string($localResource) && !empty($remoteResource) && !empty($localResource)) {
146 $fileContent = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($remoteResource, 0, array(TYPO3_user_agent));
147 if ($fileContent !== FALSE) {
148 if (\TYPO3\CMS\Core\Utility\GeneralUtility::writeFileToTypo3tempDir($localResource, $fileContent) !== NULL) {
149 throw new ExtensionManagerException(sprintf('Could not write to file %s.', $localResource), 1342635378);
150 }
151 } else {
152 throw new ExtensionManagerException(sprintf('Could not access remote resource %s.', $remoteResource), 1342635425);
153 }
154 }
155 }
156
157 /**
158 * Method returns location of local extension list file.
159 *
160 * @access public
161 * @return string local location of file
162 * @see getRemoteExtListFile()
163 */
164 public function getLocalExtListFile() {
165 $absFilePath = PATH_site . 'typo3temp/ExtensionManager/' . (int)$this->repository->getUid() . '.extensions.xml.gz';
166 return $absFilePath;
167 }
168
169 /**
170 * Method returns location of remote extension list file.
171 *
172 * @access public
173 * @return string remote location of file
174 * @see getLocalExtListFile()
175 */
176 public function getRemoteExtListFile() {
177 $mirror = $this->getMirrors(TRUE)->getMirror();
178 $filePath = 'http://' . $mirror['host'] . $mirror['path'] . 'extensions.xml.gz';
179 return $filePath;
180 }
181
182 /**
183 * Method returns location of remote file containing
184 * the extension checksum hash.
185 *
186 * @access public
187 * @return string remote location of file
188 */
189 public function getRemoteExtHashFile() {
190 $mirror = $this->getMirrors(TRUE)->getMirror();
191 $filePath = 'http://' . $mirror['host'] . $mirror['path'] . 'extensions.md5';
192 return $filePath;
193 }
194
195 /**
196 * Method returns location of local mirror list file.
197 *
198 * @access public
199 * @return string local location of file
200 * @see getRemoteMirrorListFile()
201 */
202 public function getLocalMirrorListFile() {
203 $absFilePath = PATH_site . 'typo3temp/ExtensionManager/' . (int)$this->repository->getUid() . '.mirrors.xml.gz';
204 return $absFilePath;
205 }
206
207 /**
208 * Method returns location of remote mirror list file.
209 *
210 * @access public
211 * @return string remote location of file
212 * @see getLocalMirrorListFile()
213 */
214 public function getRemoteMirrorListFile() {
215 $filePath = $this->repository->getMirrorListUrl();
216 return $filePath;
217 }
218
219 /**
220 * Method returns available mirrors for registered repository.
221 *
222 * If there are no mirrors registered to the repository,
223 * the method will retrieve them from file system or remote
224 * server.
225 *
226 * @access public
227 * @param bool $forcedUpdateFromRemote if boolean TRUE, mirror configuration will always retrieved from remote server
228 * @return \TYPO3\CMS\Extensionmanager\Domain\Model\Mirrors instance of repository mirrors class
229 * @throws ExtensionManagerException
230 */
231 public function getMirrors($forcedUpdateFromRemote = TRUE) {
232 $assignedMirror = $this->repository->getMirrors();
233 if ($forcedUpdateFromRemote || is_null($assignedMirror) || !is_object($assignedMirror)) {
234 if ($forcedUpdateFromRemote || !is_file($this->getLocalMirrorListFile())) {
235 $this->fetchMirrorListFile();
236 }
237 /** @var $objMirrorListImporter \TYPO3\CMS\Extensionmanager\Utility\Importer\MirrorListUtility */
238 $objMirrorListImporter = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extensionmanager\Utility\Importer\MirrorListUtility::class);
239 $this->repository->addMirrors($objMirrorListImporter->getMirrors($this->getLocalMirrorListFile()));
240 }
241 return $this->repository->getMirrors();
242 }
243
244 /**
245 * Method returns information if currently available
246 * extension list might be outdated.
247 *
248 * @access public
249 * @see Tx_Extensionmanager_Utility_Repository_Helper::PROBLEM_NO_VERSIONS_IN_DATABASE,
250 * @throws ExtensionManagerException
251 * @return int "0" if everything is perfect, otherwise bitmask with problems
252 */
253 public function isExtListUpdateNecessary() {
254 $updateNecessity = 0;
255 if ($this->extensionRepository->countByRepository($this->repository->getUid()) <= 0) {
256 $updateNecessity |= self::PROBLEM_NO_VERSIONS_IN_DATABASE;
257 }
258 if (!is_file($this->getLocalExtListFile())) {
259 $updateNecessity |= self::PROBLEM_EXTENSION_FILE_NOT_EXISTING;
260 } else {
261 $remotemd5 = \TYPO3\CMS\Core\Utility\GeneralUtility::getUrl($this->getRemoteExtHashFile(), 0, array(TYPO3_user_agent));
262 if ($remotemd5 !== FALSE) {
263 $localmd5 = md5_file($this->getLocalExtListFile());
264 if ($remotemd5 !== $localmd5) {
265 $updateNecessity |= self::PROBLEM_EXTENSION_HASH_CHANGED;
266 }
267 } else {
268 throw new ExtensionManagerException('Could not retrieve extension hash file from remote server.', 1342635016);
269 }
270 }
271 return $updateNecessity;
272 }
273
274 /**
275 * Method updates TYPO3 database with up-to-date
276 * extension version records.
277 *
278 * @return bool TRUE if the extension list was successfully update, FALSE if no update necessary
279 * @throws ExtensionManagerException
280 * @see isExtListUpdateNecessary()
281 */
282 public function updateExtList() {
283 $updated = FALSE;
284 $updateNecessity = $this->isExtListUpdateNecessary();
285 if ($updateNecessity !== 0) {
286 // retrieval of file necessary
287 $tmpBitmask = self::PROBLEM_EXTENSION_FILE_NOT_EXISTING | self::PROBLEM_EXTENSION_HASH_CHANGED;
288 if (($tmpBitmask & $updateNecessity) > 0) {
289 $this->fetchExtListFile();
290 $updateNecessity &= ~$tmpBitmask;
291 }
292 // database table cleanup
293 if ($updateNecessity & self::PROBLEM_NO_VERSIONS_IN_DATABASE) {
294 $updateNecessity &= ~self::PROBLEM_NO_VERSIONS_IN_DATABASE;
295 } else {
296 // Use straight query as extbase "remove" is too slow here
297 // This truncates the whole table. It would be more correct to remove only rows of a specific
298 // repository, but multiple repository handling is not implemented, and truncate is quicker.
299 $this->getDatabaseConnection()->exec_TRUNCATEquery('tx_extensionmanager_domain_model_extension');
300 }
301 // no further problems - start of import process
302 if ($updateNecessity === 0) {
303 $uid = $this->repository->getUid();
304 /* @var $objExtListImporter \TYPO3\CMS\Extensionmanager\Utility\Importer\ExtensionListUtility */
305 $objExtListImporter = $this->objectManager->get(\TYPO3\CMS\Extensionmanager\Utility\Importer\ExtensionListUtility::class);
306 $objExtListImporter->import($this->getLocalExtListFile(), $uid);
307 $updated = TRUE;
308 }
309 }
310 return $updated;
311 }
312
313 /**
314 * Get database connection
315 *
316 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
317 */
318 protected function getDatabaseConnection() {
319 return $GLOBALS['TYPO3_DB'];
320 }
321
322 }