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