9e236840c6ba7a7edbb7770a23ea486725366394
[Packages/TYPO3.CMS.git] / typo3 / sysext / em / classes / repository / class.tx_em_repository_utility.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2010 Marcus Krause <marcus#exp2010@t3sec.info>
6 * Steffen Kamper <info@sk-typo3.de>
7 * All rights reserved
8 *
9 * This script is part of the TYPO3 project. The TYPO3 project is
10 * free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * The GNU General Public License can be found at
16 * http://www.gnu.org/copyleft/gpl.html.
17 *
18 * This script is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * This copyright notice MUST APPEAR in all copies of the script!
24 ***************************************************************/
25 /**
26 * class.tx_em_repository_utility.php
27 *
28 * Module: Extension manager - Central repository utility functions
29 *
30 * @author Marcus Krause <marcus#exp2010@t3sec.info>
31 * @author Steffen Kamper <info@sk-typo3.de>
32 */
33
34
35 /**
36 * Central utility class for repository handling.
37 *
38 * @author Marcus Krause <marcus#exp2010@t3sec.info>
39 * @author Steffen Kamper <info@sk-typo3.de>
40 *
41 * @since 2010-02-18
42 * @package TYPO3
43 * @subpackage EM
44 */
45 class tx_em_Repository_Utility implements t3lib_Singleton {
46
47
48 /**
49 * ##########################################
50 * Problem constants - to be used in bitmasks
51 * ##########################################
52 */
53 /**
54 * Type of problem: extension file not existing in file system.
55 *
56 * @var integer
57 */
58 const PROBLEM_EXTENSION_FILE_NOT_EXISTING = 1;
59
60 /**
61 * Type of problem: wrong hash indicates outdated extension file.
62 *
63 * @var integer
64 */
65 const PROBLEM_EXTENSION_HASH_CHANGED = 2;
66
67 /**
68 * Type of problem: no version records in database.
69 *
70 * @var integer
71 */
72 const PROBLEM_NO_VERSIONS_IN_DATABASE = 4;
73
74
75 /**
76 * Keeps instance of repository class.
77 *
78 * @var tx_em_Repository
79 */
80 protected $repository = NULL;
81
82
83 /**
84 * Class constructor.
85 *
86 * @access public
87 * @param object &$repository (optional) instance of {@link tx_em_Repository repository} class
88 * @return void
89 */
90 function __construct(&$repository = NULL) {
91 if ($repository !== NULL && is_object($repository)
92 && $repository instanceof tx_em_Repository) {
93 $this->setRepository($repository);
94 }
95 }
96
97 /**
98 * Method provides a wrapper for throwing an exception.
99 *
100 * @access protected
101 * @see tx_em_ConnectionException
102 * @param string $message the exception message to throw.
103 * @param integer $code the exception code.
104 * @return void
105 */
106 protected function throwConnectionException($message = "", $code = 0) {
107 throw new tx_em_ConnectionException(get_class($this) . ': ' . $message, $code);
108 }
109
110 /**
111 * Method registers required repository instance to work with.
112 *
113 * Repository instance is passed by reference.
114 *
115 * @access public
116 * @param tx_em_Repository &$repository instance of {@link tx_em_Repository repository} class
117 * @return void
118 * @see $repository
119 */
120 public function setRepository(tx_em_Repository &$repository) {
121 $this->repository = $repository;
122 }
123
124 /**
125 * Method fetches extension list file from remote server.
126 *
127 * Delegates to {@link fetchFile()}.
128 *
129 * @access public
130 * @return void
131 * @see fetchFile()
132 */
133 public function fetchExtListFile() {
134 $this->fetchFile($this->getRemoteExtListFile(), $this->getLocalExtListFile());
135 }
136
137 /**
138 * Method fetches mirror list file from remote server.
139 *
140 * Delegates to {@link fetchFile()}.
141 *
142 * @access public
143 * @return void
144 * @see fetchFile()
145 */
146 public function fetchMirrorListFile() {
147 $this->fetchFile($this->getRemoteMirrorListFile(), $this->getLocalMirrorListFile());
148 }
149
150 /**
151 * Method fetches contents from remote server and
152 * writes them into a file in the local file system.
153 *
154 * @access protected
155 * @param string $remoteRessource remote ressource to read contents from
156 * @param string $localRessource local ressource (absolute file path) to store retrieved contents to
157 * @return void
158 * @see t3lib_div::getUrl(), t3lib_div::writeFile()
159 * @throws tx_em_ConnectionException
160 */
161 protected function fetchFile($remoteRessource, $localRessource) {
162 if (is_string($remoteRessource) && is_string($localRessource)
163 && !empty($remoteRessource) && !empty($localRessource)) {
164 $fileContent = t3lib_div::getUrl($remoteRessource, 0, array(TYPO3_user_agent));
165 if ($fileContent !== FALSE) {
166 t3lib_div::writeFile($localRessource, $fileContent) || $this->throwConnectionException(sprintf('Could not write to file %s.', htmlspecialchars($localRessource)));
167 } else {
168 $this->throwConnectionException(sprintf('Could not access remote ressource %s.', htmlspecialchars($remoteRessource)));
169 }
170 }
171 }
172
173 /**
174 * Method returns location of local extension list file.
175 *
176 * @access public
177 * @return string local location of file
178 * @see getRemoteExtListFile()
179 */
180 public function getLocalExtListFile() {
181 $absFilePath = PATH_site . 'typo3temp/'
182 . intval($this->repository->getId())
183 . '.extensions.xml.gz';
184 return $absFilePath;
185 }
186
187 /**
188 * Method returns location of remote extension list file.
189 *
190 * @access public
191 * @return string remote location of file
192 * @see getLocalExtListFile()
193 */
194 public function getRemoteExtListFile() {
195 $mirror = $this->getMirrors(TRUE)->getMirror();
196 $filePath = 'http://' . $mirror['host'] . $mirror['path']
197 . 'extensions.xml.gz';
198 return $filePath;
199 }
200
201 /**
202 * Method returns location of remote file containing
203 * the extension checksum hash.
204 *
205 * @access public
206 * @return string remote location of file
207 */
208 public function getRemoteExtHashFile() {
209 $mirror = $this->getMirrors(TRUE)->getMirror();
210 $filePath = 'http://' . $mirror['host'] . $mirror['path']
211 . 'extensions.md5';
212 return $filePath;
213 }
214
215 /**
216 * Method returns location of local mirror list file.
217 *
218 * @access public
219 * @return string local location of file
220 * @see getRemoteMirrorListFile()
221 */
222 public function getLocalMirrorListFile() {
223 $absFilePath = PATH_site . 'typo3temp/'
224 . intval($this->repository->getId())
225 . '.mirrors.xml.gz';
226 return $absFilePath;
227 }
228
229 /**
230 * Method returns location of remote mirror list file.
231 *
232 * @access public
233 * @return string remote location of file
234 * @see getLocalMirrorListFile()
235 */
236 public function getRemoteMirrorListFile() {
237 $filePath = $this->repository->getMirrorListUrl();
238 return $filePath;
239 }
240
241 /**
242 * Method returns available mirrors for registered repository.
243 *
244 * If there are no mirrors registered to the repository,
245 * the method will retrieve them from file system or remote
246 * server.
247 *
248 * @access public
249 * @param boolean $forcedUpdateFromRemote if boolean TRUE, mirror configuration will always retrieved from remote server
250 * @return tx_em_Repository_Mirrors instance of repository mirrors class
251 */
252 public function getMirrors($forcedUpdateFromRemote = TRUE) {
253 $assignedMirror = $this->repository->getMirrors();
254 if ($forcedUpdateFromRemote || is_null($assignedMirror) || !is_object($assignedMirror)) {
255 if ($forcedUpdateFromRemote || !is_file($this->getLocalMirrorListFile())) {
256 $this->fetchMirrorListFile();
257 }
258 /** @var $objMirrorListImporter tx_em_Import_MirrorListImporter */
259 $objMirrorListImporter = t3lib_div::makeInstance('tx_em_Import_MirrorListImporter');
260 $this->repository->addMirrors($objMirrorListImporter->getMirrors($this->getLocalMirrorListFile()));
261 }
262 return $this->repository->getMirrors();
263 }
264
265 /**
266 * Method returns information if currently available
267 * extension list might be outdated.
268 *
269 * @access public
270 * @see tx_em_Repository_Utility::PROBLEM_NO_VERSIONS_IN_DATABASE,
271 * tx_em_Repository_Utility::PROBLEM_EXTENSION_FILE_NOT_EXISTING,
272 * tx_em_Repository_Utility::PROBLEM_EXTENSION_HASH_CHANGED
273 * @return integer integer "0" if everything is perfect, otherwise bitmask with occured problems
274 * @see updateExtList()
275 */
276 public function isExtListUpdateNecessary() {
277 $updateNecessity = 0;
278
279 if (tx_em_Database::getExtensionCountFromRepository($this->getRepositoryUID(TRUE)) <= 0) {
280 $updateNecessity |= self::PROBLEM_NO_VERSIONS_IN_DATABASE;
281 }
282
283 if (!is_file($this->getLocalExtListFile())) {
284 $updateNecessity |= self::PROBLEM_EXTENSION_FILE_NOT_EXISTING;
285 } else {
286 $remotemd5 = t3lib_div::getUrl($this->getRemoteExtHashFile(), 0, array(TYPO3_user_agent));
287
288 if ($remotemd5 !== FALSE) {
289 $localmd5 = md5_file($this->getLocalExtListFile());
290 if ($remotemd5 !== $localmd5) {
291 $updateNecessity |= self::PROBLEM_EXTENSION_HASH_CHANGED;
292 }
293 } else {
294 $this->throwConnectionException('Could not retrieve extension hash file from remote server.');
295 }
296 }
297 return $updateNecessity;
298 }
299
300 /**
301 * Method returns UID of the current repository.
302 *
303 * @access public
304 * @param boolean $insertIfMissing creates repository record in DB if set to TRUE
305 * @return integer
306 */
307 public function getRepositoryUID($insertIfMissing = FALSE) {
308 $uid = $this->repository->getId();
309 $repository = tx_em_Database::getRepositoryByUID($uid);
310 if (empty($repository) && $insertIfMissing) {
311 $uid = tx_em_Database::insertRepository($this->repository);
312 } else {
313 $uid = intval($repository['uid']);
314 }
315
316 return $uid;
317 }
318
319 /**
320 * Method updates TYPO3 database with up-to-date
321 * extension version records.
322 *
323 * @access public
324 * @param boolean $renderFlashMessage if method should return flashmessage or raw integer
325 * @return mixed either sum of imported extensions or instance of t3lib_FlashMessage
326 * @see isExtListUpdateNecessary()
327 */
328 public function updateExtList($renderFlashMessage = FALSE) {
329
330 if ($renderFlashMessage) {
331 /* @var $flashMessage t3lib_FlashMessage */
332 $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage',
333 $GLOBALS['LANG']->getLL('ext_import_list_unchanged_header'),
334 $GLOBALS['LANG']->getLL('ext_import_list_unchanged'),
335 t3lib_FlashMessage::INFO
336 );
337 }
338 $sumRecords = 0;
339
340 $updateNecessity = $this->isExtListUpdateNecessary();
341
342 if ($updateNecessity !== 0) {
343 // retrieval of file necessary
344 $tmpBitmask = (self::PROBLEM_EXTENSION_FILE_NOT_EXISTING | self::PROBLEM_EXTENSION_HASH_CHANGED);
345 if (($tmpBitmask & $updateNecessity) > 0) {
346 $this->fetchExtListFile();
347 $updateNecessity &= ~$tmpBitmask;
348 }
349
350 // database table cleanup
351 if (($updateNecessity & self::PROBLEM_NO_VERSIONS_IN_DATABASE)) {
352 $updateNecessity &= ~self::PROBLEM_NO_VERSIONS_IN_DATABASE;
353 } else {
354 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_extensions', 'repository=' . $this->getRepositoryUID());
355 }
356
357 // no further problems - start of import process
358 if ($updateNecessity === 0) {
359 $uid = $this->getRepositoryUID(TRUE);
360 /* @var $objExtListImporter tx_em_Import_ExtensionListImporter */
361 $objExtListImporter = t3lib_div::makeInstance('tx_em_Import_ExtensionListImporter');
362 $objExtListImporter->import($this->getLocalExtListFile(), $uid);
363 $sumRecords = tx_em_Database::getExtensionCountFromRepository($uid);
364 if ($renderFlashMessage) {
365 $flashMessage->setTitle($GLOBALS['LANG']->getLL('ext_import_extlist_updated_header'));
366 $flashMessage->setMessage(sprintf($GLOBALS['LANG']->getLL('ext_import_extlist_updated'), tx_em_Database::getExtensionCountFromRepository()));
367 $flashMessage->setSeverity(t3lib_FlashMessage::OK);
368 }
369 }
370 }
371 return $renderFlashMessage ? $flashMessage : $sumRecords;
372 }
373 }
374
375 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['typo3/sysext/em/classes/repository/class.tx_em_repository_utility.php'])) {
376 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['typo3/sysext/em/classes/repository/class.tx_em_repository_utility.php']);
377 }
378
379 ?>