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