Fixed bug #5919: Loss of constraints during upload to TER
[Packages/TYPO3.CMS.git] / typo3 / mod / tools / em / class.em_terconnection.php
1 <?php
2 /* **************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2005 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * (c) 2006 Karsten Dambekalns <karsten@typo3.org>
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 * A copy is found in the textfile GPL.txt and important notices to the license
18 * from the author is found in LICENSE.txt distributed with these scripts.
19 *
20 *
21 * This script is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * This copyright notice MUST APPEAR in all copies of the script!
27 ***************************************************************/
28
29
30 if (!defined('SOAP_1_2')) {
31 require_once('class.nusoap.php');
32 # require_once('/usr/share/php/SOAP/Client.php');
33 }
34 require_once('class.em_soap.php');
35
36 /**
37 * TER2 connection handling class for the TYPO3 Extension Manager.
38 *
39 * It contains methods for downloading and uploading extensions and related code
40 *
41 * @author Karsten Dambekalns <karsten@typo3.org>
42 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
43 * @package TYPO3
44 * @subpackage EM
45 */
46 class SC_mod_tools_em_terconnection {
47 var $wsdlURL;
48
49 /**
50 * Extension manager module
51 *
52 * @var SC_mod_tools_em_index
53 */
54 var $emObj;
55
56 /**
57 * Fetches an extension from the given mirror
58 *
59 * @param string $extKey Extension Key
60 * @param string $version Version to install
61 * @param string $expectedMD5 Expected MD5 hash of extension file
62 * @param string $mirrorURL URL of mirror to use
63 * @return mixed T3X data (array) or error message (string)
64 */
65 function fetchExtension($extKey, $version, $expectedMD5, $mirrorURL) {
66 $mirrorURL .= $extKey{0}.'/'.$extKey{1}.'/'.$extKey.'_'.$version.'.t3x';
67 $t3x = t3lib_div::getURL($mirrorURL);
68 $MD5 = md5($t3x);
69
70 if($t3x===false) return 'The T3X file could not be fetched. Possible reasons: network problems, allow_url_fopen is off, curl is not enabled in Install tool.';
71
72 if($MD5 == $expectedMD5) {
73 // Fetch and return:
74 return $this->decodeExchangeData($t3x);
75 } else {
76 return 'Error: MD5 hash of downloaded file not as expected:<br />'.$MD5.' != '.$expectedMD5;
77 }
78 }
79
80 /**
81 * Fetches an extensions l10n file from the given mirror
82 *
83 * @param string $extKey Extension Key
84 * @param string $lang The language code of the translation to fetch
85 * @param string $mirrorURL URL of mirror to use
86 * @return mixed Array containing l10n data or error message (string)
87 */
88 function fetchTranslation($extKey, $lang, $mirrorURL) {
89 $mirrorURL .= $extKey{0}.'/'.$extKey{1}.'/'.$extKey.'-l10n/'.$extKey.'-l10n-'.$lang.'.zip';
90 $l10n = t3lib_div::getURL($mirrorURL);
91
92 if($l10n !== false) {
93 return array($l10n);
94 } else {
95 return 'Error: Translation could not be fetched.';
96 }
97 }
98
99 /**
100 * Fetches extension l10n status from the given mirror
101 *
102 * @param string $extKey Extension Key
103 * @param string $mirrorURL URL of mirror to use
104 * @return mixed Array containing l10n status data or FALSE if no status could be fetched
105 */
106 function fetchTranslationStatus($extKey, $mirrorURL) {
107
108 $url = $mirrorURL . $extKey{0}.'/'.$extKey{1}.'/'.$extKey.'-l10n/'.$extKey.'-l10n.xml';
109 $remote = t3lib_div::getURL($url);
110
111 if($remote !== false) {
112 $parsed = $this->emObj->xmlhandler->parseL10nXML($remote);
113 return $parsed['languagePackIndex'];
114 }
115
116 return FALSE;
117 }
118
119 /**
120 * Decode server data
121 * This is information like the extension list, extension information etc., return data after uploads (new em_conf)
122 *
123 * @param string Data stream from remove server
124 * @return mixed On success, returns an array with data array and stats array as key 0 and 1. Otherwise returns error string
125 * @see fetchServerData(), processRepositoryReturnData()
126 */
127 function decodeServerData($externalData) {
128 $parts = explode(':',$externalData,4);
129 $dat = base64_decode($parts[2]);
130 // compare hashes ignoring any leading whitespace. See bug #0000365.
131 if (ltrim($parts[0])==md5($dat)) {
132 if ($parts[1]=='gzcompress') {
133 if (function_exists('gzuncompress')) {
134 $dat = gzuncompress($dat);
135 } else return 'Decoding Error: No decompressor available for compressed content. gzuncompress() function is not available!';
136 }
137 $listArr = unserialize($dat);
138
139 if (is_array($listArr)) {
140 return $listArr;
141 } else {
142 return 'Error: Unserialized information was not an array - strange!';
143 }
144 } else return 'Error: MD5 hashes in T3X data did not match!';
145 }
146
147 /**
148 * Decodes extension upload array.
149 * This kind of data is when an extension is uploaded to TER
150 *
151 * @param string Data stream
152 * @return mixed Array with result on success, otherwise an error string.
153 */
154 function decodeExchangeData($str) {
155 $parts = explode(':',$str,3);
156 if ($parts[1]=='gzcompress') {
157 if (function_exists('gzuncompress')) {
158 $parts[2] = gzuncompress($parts[2]);
159 } else return 'Decoding Error: No decompressor available for compressed content. gzcompress()/gzuncompress() functions are not available!';
160 }
161 if (md5($parts[2]) == $parts[0]) {
162 $output = unserialize($parts[2]);
163 if (is_array($output)) {
164 return array($output,'');
165 } else return 'Error: Content could not be unserialized to an array. Strange (since MD5 hashes match!)';
166 } else return 'Error: MD5 mismatch. Maybe the extension file was downloaded and saved as a text file by the browser and thereby corrupted!? (Always select "All" filetype when saving extensions)';
167 }
168
169
170 /**
171 * Encodes extension upload array
172 *
173 * @param array Array containing extension
174 * @return string Content stream
175 */
176 function makeUploadDataFromArray($uploadArray) {
177 if (is_array($uploadArray)) {
178 $serialized = serialize($uploadArray);
179 $md5 = md5($serialized);
180
181 $content = $md5.':';
182 $content.= 'gzcompress:';
183 $content.= gzcompress($serialized);
184 }
185 return $content;
186 }
187
188 /**
189 * [Describe function...]
190 *
191 * @param [type] $em: ...
192 * @return [type] ...
193 */
194 function uploadToTER($em) {
195 $uArr = $this->emObj->makeUploadArray($em['extKey'],$em['extInfo']);
196 if(!is_array($uArr)) return $uArr;
197
198 // Render new version number:
199 $newVersionBase = $em['extInfo']['EM_CONF']['version'];
200 switch((string)$em['upload']['mode']) {
201 case 'new_dev':
202 $cmd='dev';
203 break;
204 case 'new_sub':
205 $cmd='sub';
206 break;
207 case 'new_main':
208 $cmd='main';
209 break;
210 case 'custom':
211 $newVersionBase = $em['upload']['version'];
212 case 'latest':
213 default:
214 $cmd='';
215 break;
216 }
217 $versionArr = $this->emObj->renderVersion($newVersionBase, $cmd);
218 $em['version'] = $versionArr['version'];
219
220 // Create dependency / conflict information:
221 $dependenciesArr = array ();
222 $extKeysArr = $uArr['EM_CONF']['constraints']['depends'];
223
224 if (is_array($extKeysArr)) {
225 foreach ($extKeysArr as $extKey => $version) {
226 if (strlen($extKey)) {
227 $dependenciesArr[] = array (
228 'kind' => 'depends',
229 'extensionKey' => utf8_encode($extKey),
230 'versionRange' => utf8_encode($version),
231 );
232 }
233 }
234 }
235
236 $extKeysArr = $uArr['EM_CONF']['constraints']['conflicts'];
237 if (is_array($extKeysArr)) {
238 foreach ($extKeysArr as $extKey => $version) {
239 if (strlen($extKey)) {
240 $dependenciesArr[] = array (
241 'kind' => 'conflicts',
242 'extensionKey' => utf8_encode($extKey),
243 'versionRange' => utf8_encode($version),
244 );
245 }
246 }
247 }
248 // FIXME: This part must be removed, when the problem is solved on the TER-Server #5919
249 if (count($dependenciesArr) == 1) {
250 $dependenciesArr[] = array (
251 'kind' => 'depends',
252 'extensionKey' => '',
253 'versionRange' => '',
254 );
255 }
256 // END for Bug #5919
257
258 // Compile data for SOAP call:
259 $accountData = array(
260 'username' => $em['user']['fe_u'],
261 'password' => $em['user']['fe_p']
262 );
263 $extensionData = array (
264 'extensionKey' => utf8_encode($em['extKey']),
265 'version' => utf8_encode($em['version']),
266 'metaData' => array (
267 'title' => utf8_encode($uArr['EM_CONF']['title']),
268 'description' => utf8_encode($uArr['EM_CONF']['description']),
269 'category' => utf8_encode($uArr['EM_CONF']['category']),
270 'state' => utf8_encode($uArr['EM_CONF']['state']),
271 'authorName' => utf8_encode($uArr['EM_CONF']['author']),
272 'authorEmail' => utf8_encode($uArr['EM_CONF']['author_email']),
273 'authorCompany' => utf8_encode($uArr['EM_CONF']['author_company']),
274 ),
275 'technicalData' => array (
276 'dependencies' => $dependenciesArr,
277 'loadOrder' => utf8_encode($uArr['EM_CONF']['loadOrder']),
278 'uploadFolder' => (boolean) intval($uArr['EM_CONF']['uploadfolder']),
279 'createDirs' => utf8_encode($uArr['EM_CONF']['createDirs']),
280 'shy' => (boolean) intval($uArr['EM_CONF']['shy']),
281 'modules' => utf8_encode($uArr['EM_CONF']['module']),
282 'modifyTables' => utf8_encode($uArr['EM_CONF']['modify_tables']),
283 'priority' => utf8_encode($uArr['EM_CONF']['priority']),
284 'clearCacheOnLoad' => (boolean) intval($uArr['EM_CONF']['clearCacheOnLoad']),
285 'lockType' => utf8_encode($uArr['EM_CONF']['lockType']),
286 ),
287 'infoData' => array(
288 'codeLines' => intval($uArr['misc']['codelines']),
289 'codeBytes' => intval($uArr['misc']['codebytes']),
290 'codingGuidelinesCompliance' => utf8_encode($uArr['EM_CONF']['CGLcompliance']),
291 'codingGuidelinesComplianceNotes' => utf8_encode($uArr['EM_CONF']['CGLcompliance_note']),
292 'uploadComment' => utf8_encode($em['upload']['comment']),
293 'techInfo' => $uArr['techInfo'],
294 ),
295 );
296
297 $filesData = array();
298 foreach ($uArr['FILES'] as $filename => $infoArr) {
299 $content = (!defined('SOAP_1_2') && class_exists('soapclient')) ? base64_encode($infoArr['content']) : $infoArr['content']; // bug in NuSOAP - no automatic encoding
300 $filesData['fileData'][] = array (
301 'name' => utf8_encode($infoArr['name']),
302 'size' => intval($infoArr['size']),
303 'modificationTime' => intval($infoArr['mtime']),
304 'isExecutable' => intval($infoArr['is_executable']),
305 'content' => $content,
306 'contentMD5' => $infoArr['content_md5'],
307 );
308 }
309
310 $soap = t3lib_div::makeInstance('em_soap');
311 $soap->init(array('wsdl'=>$this->wsdlURL,'soapoptions'=> array('trace'=>1,'exceptions'=>0)));
312 $response = $soap->call('uploadExtension', array('accountData' => $accountData, 'extensionData' => $extensionData, 'filesData' => $filesData));
313
314 if($response===false) {
315 switch(true) {
316 case is_string($soap->error):
317 return $soap->error;
318 break;
319 default:
320 return $soap->error->faultstring;
321 }
322 }
323
324 return $response;
325 }
326 }
327 ?>