1 | <?php |
||||
2 | |||||
3 | /** |
||||
4 | * This file is the main Package Manager. |
||||
5 | * |
||||
6 | * Simple Machines Forum (SMF) |
||||
7 | * |
||||
8 | * @package SMF |
||||
9 | * @author Simple Machines http://www.simplemachines.org |
||||
10 | * @copyright 2019 Simple Machines and individual contributors |
||||
11 | * @license http://www.simplemachines.org/about/smf/license.php BSD |
||||
12 | * |
||||
13 | * @version 2.1 RC2 |
||||
14 | */ |
||||
15 | |||||
16 | if (!defined('SMF')) |
||||
17 | die('No direct access...'); |
||||
18 | |||||
19 | /** |
||||
20 | * This is the notoriously defunct package manager..... :/. |
||||
21 | */ |
||||
22 | function Packages() |
||||
23 | { |
||||
24 | global $txt, $sourcedir, $context; |
||||
25 | |||||
26 | // @todo Remove this! |
||||
27 | if (isset($_GET['get']) || isset($_GET['pgdownload'])) |
||||
28 | { |
||||
29 | require_once($sourcedir . '/PackageGet.php'); |
||||
30 | return PackageGet(); |
||||
31 | } |
||||
32 | |||||
33 | isAllowedTo('admin_forum'); |
||||
34 | |||||
35 | // Load all the basic stuff. |
||||
36 | require_once($sourcedir . '/Subs-Package.php'); |
||||
37 | loadLanguage('Packages'); |
||||
38 | loadTemplate('Packages', 'admin'); |
||||
39 | |||||
40 | $context['page_title'] = $txt['package']; |
||||
41 | |||||
42 | // Delegation makes the world... that is, the package manager go 'round. |
||||
43 | $subActions = array( |
||||
44 | 'browse' => 'PackageBrowse', |
||||
45 | 'remove' => 'PackageRemove', |
||||
46 | 'list' => 'PackageList', |
||||
47 | 'ftptest' => 'PackageFTPTest', |
||||
48 | 'install' => 'PackageInstallTest', |
||||
49 | 'install2' => 'PackageInstall', |
||||
50 | 'uninstall' => 'PackageInstallTest', |
||||
51 | 'uninstall2' => 'PackageInstall', |
||||
52 | 'options' => 'PackageOptions', |
||||
53 | 'perms' => 'PackagePermissions', |
||||
54 | 'flush' => 'FlushInstall', |
||||
55 | 'examine' => 'ExamineFile', |
||||
56 | 'showoperations' => 'ViewOperations', |
||||
57 | ); |
||||
58 | |||||
59 | // Work out exactly who it is we are calling. |
||||
60 | if (isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']])) |
||||
61 | $context['sub_action'] = $_REQUEST['sa']; |
||||
62 | else |
||||
63 | $context['sub_action'] = 'browse'; |
||||
64 | |||||
65 | // Set up some tabs... |
||||
66 | $context[$context['admin_menu_name']]['tab_data'] = array( |
||||
67 | 'title' => $txt['package_manager'], |
||||
68 | // @todo 'help' => 'registrations', |
||||
69 | 'description' => $txt['package_manager_desc'], |
||||
70 | 'tabs' => array( |
||||
71 | 'browse' => array( |
||||
72 | ), |
||||
73 | 'packageget' => array( |
||||
74 | 'description' => $txt['download_packages_desc'], |
||||
75 | ), |
||||
76 | 'perms' => array( |
||||
77 | 'description' => $txt['package_file_perms_desc'], |
||||
78 | ), |
||||
79 | 'options' => array( |
||||
80 | 'description' => $txt['package_install_options_desc'], |
||||
81 | ), |
||||
82 | ), |
||||
83 | ); |
||||
84 | |||||
85 | if ($context['sub_action'] == 'browse') |
||||
86 | loadJavaScriptFile('suggest.js', array('defer' => false, 'minimize' => true), 'smf_suggest'); |
||||
87 | |||||
88 | call_integration_hook('integrate_manage_packages', array(&$subActions)); |
||||
89 | |||||
90 | // Call the function we're handing control to. |
||||
91 | call_helper($subActions[$context['sub_action']]); |
||||
92 | } |
||||
93 | |||||
94 | /** |
||||
95 | * Test install a package. |
||||
96 | */ |
||||
97 | function PackageInstallTest() |
||||
98 | { |
||||
99 | global $boarddir, $txt, $context, $scripturl, $sourcedir, $packagesdir, $modSettings, $smcFunc, $settings; |
||||
100 | |||||
101 | // You have to specify a file!! |
||||
102 | if (!isset($_REQUEST['package']) || $_REQUEST['package'] == '') |
||||
103 | redirectexit('action=admin;area=packages'); |
||||
104 | $context['filename'] = preg_replace('~[\.]+~', '.', $_REQUEST['package']); |
||||
105 | |||||
106 | // Do we have an existing id, for uninstalls and the like. |
||||
107 | $context['install_id'] = isset($_REQUEST['pid']) ? (int) $_REQUEST['pid'] : 0; |
||||
108 | |||||
109 | require_once($sourcedir . '/Subs-Package.php'); |
||||
110 | |||||
111 | // Load up the package FTP information? |
||||
112 | create_chmod_control(); |
||||
113 | |||||
114 | // Make sure temp directory exists and is empty. |
||||
115 | if (file_exists($packagesdir . '/temp')) |
||||
116 | deltree($packagesdir . '/temp', false); |
||||
117 | |||||
118 | if (!mktree($packagesdir . '/temp', 0755)) |
||||
119 | { |
||||
120 | deltree($packagesdir . '/temp', false); |
||||
121 | if (!mktree($packagesdir . '/temp', 0777)) |
||||
122 | { |
||||
123 | deltree($packagesdir . '/temp', false); |
||||
124 | create_chmod_control(array($packagesdir . '/temp/delme.tmp'), array('destination_url' => $scripturl . '?action=admin;area=packages;sa=' . $_REQUEST['sa'] . ';package=' . $_REQUEST['package'], 'crash_on_error' => true)); |
||||
125 | |||||
126 | deltree($packagesdir . '/temp', false); |
||||
127 | if (!mktree($packagesdir . '/temp', 0777)) |
||||
128 | fatal_lang_error('package_cant_download', false); |
||||
129 | } |
||||
130 | } |
||||
131 | |||||
132 | $context['uninstalling'] = $_REQUEST['sa'] == 'uninstall'; |
||||
133 | |||||
134 | // Change our last link tree item for more information on this Packages area. |
||||
135 | $context['linktree'][count($context['linktree']) - 1] = array( |
||||
136 | 'url' => $scripturl . '?action=admin;area=packages;sa=browse', |
||||
137 | 'name' => $context['uninstalling'] ? $txt['package_uninstall_actions'] : $txt['install_actions'] |
||||
138 | ); |
||||
139 | $context['page_title'] .= ' - ' . ($context['uninstalling'] ? $txt['package_uninstall_actions'] : $txt['install_actions']); |
||||
140 | |||||
141 | $context['sub_template'] = 'view_package'; |
||||
142 | |||||
143 | if (!file_exists($packagesdir . '/' . $context['filename'])) |
||||
144 | { |
||||
145 | deltree($packagesdir . '/temp'); |
||||
146 | fatal_lang_error('package_no_file', false); |
||||
147 | } |
||||
148 | |||||
149 | // Extract the files so we can get things like the readme, etc. |
||||
150 | if (is_file($packagesdir . '/' . $context['filename'])) |
||||
151 | { |
||||
152 | $context['extracted_files'] = read_tgz_file($packagesdir . '/' . $context['filename'], $packagesdir . '/temp'); |
||||
153 | |||||
154 | if ($context['extracted_files'] && !file_exists($packagesdir . '/temp/package-info.xml')) |
||||
155 | foreach ($context['extracted_files'] as $file) |
||||
156 | if (basename($file['filename']) == 'package-info.xml') |
||||
157 | { |
||||
158 | $context['base_path'] = dirname($file['filename']) . '/'; |
||||
159 | break; |
||||
160 | } |
||||
161 | |||||
162 | if (!isset($context['base_path'])) |
||||
163 | $context['base_path'] = ''; |
||||
164 | } |
||||
165 | elseif (is_dir($packagesdir . '/' . $context['filename'])) |
||||
166 | { |
||||
167 | copytree($packagesdir . '/' . $context['filename'], $packagesdir . '/temp'); |
||||
168 | $context['extracted_files'] = listtree($packagesdir . '/temp'); |
||||
169 | $context['base_path'] = ''; |
||||
170 | } |
||||
171 | else |
||||
172 | fatal_lang_error('no_access', false); |
||||
173 | |||||
174 | // Load up any custom themes we may want to install into... |
||||
175 | $request = $smcFunc['db_query']('', ' |
||||
176 | SELECT id_theme, variable, value |
||||
177 | FROM {db_prefix}themes |
||||
178 | WHERE (id_theme = {int:default_theme} OR id_theme IN ({array_int:known_theme_list})) |
||||
179 | AND variable IN ({string:name}, {string:theme_dir})', |
||||
180 | array( |
||||
181 | 'known_theme_list' => explode(',', $modSettings['knownThemes']), |
||||
182 | 'default_theme' => 1, |
||||
183 | 'name' => 'name', |
||||
184 | 'theme_dir' => 'theme_dir', |
||||
185 | ) |
||||
186 | ); |
||||
187 | $theme_paths = array(); |
||||
188 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||||
189 | $theme_paths[$row['id_theme']][$row['variable']] = $row['value']; |
||||
190 | $smcFunc['db_free_result']($request); |
||||
191 | |||||
192 | // Get the package info... |
||||
193 | $packageInfo = getPackageInfo($context['filename']); |
||||
194 | |||||
195 | if (!is_array($packageInfo)) |
||||
196 | fatal_lang_error($packageInfo); |
||||
197 | |||||
198 | $packageInfo['filename'] = $context['filename']; |
||||
199 | $context['package_name'] = isset($packageInfo['name']) ? $packageInfo['name'] : $context['filename']; |
||||
200 | |||||
201 | // Set the type of extraction... |
||||
202 | $context['extract_type'] = isset($packageInfo['type']) ? $packageInfo['type'] : 'modification'; |
||||
203 | |||||
204 | // The mod isn't installed.... unless proven otherwise. |
||||
205 | $context['is_installed'] = false; |
||||
206 | |||||
207 | // See if it is installed? |
||||
208 | $request = $smcFunc['db_query']('', ' |
||||
209 | SELECT version, themes_installed, db_changes |
||||
210 | FROM {db_prefix}log_packages |
||||
211 | WHERE package_id = {string:current_package} |
||||
212 | AND install_state != {int:not_installed} |
||||
213 | ORDER BY time_installed DESC |
||||
214 | LIMIT 1', |
||||
215 | array( |
||||
216 | 'not_installed' => 0, |
||||
217 | 'current_package' => $packageInfo['id'], |
||||
218 | ) |
||||
219 | ); |
||||
220 | |||||
221 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||||
222 | { |
||||
223 | $old_themes = explode(',', $row['themes_installed']); |
||||
224 | $old_version = $row['version']; |
||||
225 | $db_changes = empty($row['db_changes']) ? array() : $smcFunc['json_decode']($row['db_changes'], true); |
||||
226 | } |
||||
227 | $smcFunc['db_free_result']($request); |
||||
228 | |||||
229 | $context['database_changes'] = array(); |
||||
230 | if (isset($packageInfo['uninstall']['database'])) |
||||
231 | $context['database_changes'][] = sprintf($txt['package_db_code'], $packageInfo['uninstall']['database']); |
||||
232 | if (!empty($db_changes)) |
||||
233 | { |
||||
234 | foreach ($db_changes as $change) |
||||
235 | { |
||||
236 | if (isset($change[2]) && isset($txt['package_db_' . $change[0]])) |
||||
237 | $context['database_changes'][] = sprintf($txt['package_db_' . $change[0]], $change[1], $change[2]); |
||||
238 | elseif (isset($txt['package_db_' . $change[0]])) |
||||
239 | $context['database_changes'][] = sprintf($txt['package_db_' . $change[0]], $change[1]); |
||||
240 | else |
||||
241 | $context['database_changes'][] = $change[0] . '-' . $change[1] . (isset($change[2]) ? '-' . $change[2] : ''); |
||||
242 | } |
||||
243 | } |
||||
244 | |||||
245 | // Uninstalling? |
||||
246 | if ($context['uninstalling']) |
||||
247 | { |
||||
248 | // Wait, it's not installed yet! |
||||
249 | if (!isset($old_version) && $context['uninstalling']) |
||||
250 | { |
||||
251 | deltree($packagesdir . '/temp'); |
||||
252 | fatal_lang_error('package_cant_uninstall', false); |
||||
253 | } |
||||
254 | |||||
255 | $actions = parsePackageInfo($packageInfo['xml'], true, 'uninstall'); |
||||
256 | |||||
257 | // Gadzooks! There's no uninstaller at all!? |
||||
258 | if (empty($actions)) |
||||
259 | { |
||||
260 | deltree($packagesdir . '/temp'); |
||||
261 | fatal_lang_error('package_uninstall_cannot', false); |
||||
262 | } |
||||
263 | |||||
264 | // Can't edit the custom themes it's edited if you're uninstalling, they must be removed. |
||||
265 | $context['themes_locked'] = true; |
||||
266 | |||||
267 | // Only let them uninstall themes it was installed into. |
||||
268 | foreach ($theme_paths as $id => $data) |
||||
269 | if ($id != 1 && !in_array($id, $old_themes)) |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
![]() |
|||||
270 | unset($theme_paths[$id]); |
||||
271 | } |
||||
272 | elseif (isset($old_version) && $old_version != $packageInfo['version']) |
||||
273 | { |
||||
274 | // Look for an upgrade... |
||||
275 | $actions = parsePackageInfo($packageInfo['xml'], true, 'upgrade', $old_version); |
||||
276 | |||||
277 | // There was no upgrade.... |
||||
278 | if (empty($actions)) |
||||
279 | $context['is_installed'] = true; |
||||
280 | else |
||||
281 | { |
||||
282 | // Otherwise they can only upgrade themes from the first time around. |
||||
283 | foreach ($theme_paths as $id => $data) |
||||
284 | if ($id != 1 && !in_array($id, $old_themes)) |
||||
285 | unset($theme_paths[$id]); |
||||
286 | } |
||||
287 | } |
||||
288 | elseif (isset($old_version) && $old_version == $packageInfo['version']) |
||||
289 | $context['is_installed'] = true; |
||||
290 | |||||
291 | if (!isset($old_version) || $context['is_installed']) |
||||
292 | $actions = parsePackageInfo($packageInfo['xml'], true, 'install'); |
||||
293 | |||||
294 | $context['actions'] = array(); |
||||
295 | $context['ftp_needed'] = false; |
||||
296 | $context['has_failure'] = false; |
||||
297 | $chmod_files = array(); |
||||
298 | |||||
299 | // no actions found, return so we can display an error |
||||
300 | if (empty($actions)) |
||||
301 | return; |
||||
302 | |||||
303 | // This will hold data about anything that can be installed in other themes. |
||||
304 | $themeFinds = array( |
||||
305 | 'candidates' => array(), |
||||
306 | 'other_themes' => array(), |
||||
307 | ); |
||||
308 | |||||
309 | // Now prepare things for the template. |
||||
310 | foreach ($actions as $action) |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
311 | { |
||||
312 | // Not failed until proven otherwise. |
||||
313 | $failed = false; |
||||
314 | $thisAction = array(); |
||||
315 | |||||
316 | if ($action['type'] == 'chmod') |
||||
317 | { |
||||
318 | $chmod_files[] = $action['filename']; |
||||
319 | continue; |
||||
320 | } |
||||
321 | elseif ($action['type'] == 'readme' || $action['type'] == 'license') |
||||
322 | { |
||||
323 | $type = 'package_' . $action['type']; |
||||
324 | if (file_exists($packagesdir . '/temp/' . $context['base_path'] . $action['filename'])) |
||||
325 | $context[$type] = $smcFunc['htmlspecialchars'](trim(file_get_contents($packagesdir . '/temp/' . $context['base_path'] . $action['filename']), "\n\r")); |
||||
326 | elseif (file_exists($action['filename'])) |
||||
327 | $context[$type] = $smcFunc['htmlspecialchars'](trim(file_get_contents($action['filename']), "\n\r")); |
||||
328 | |||||
329 | if (!empty($action['parse_bbc'])) |
||||
330 | { |
||||
331 | require_once($sourcedir . '/Subs-Post.php'); |
||||
332 | $context[$type] = preg_replace('~\[[/]?html\]~i', '', $context[$type]); |
||||
333 | preparsecode($context[$type]); |
||||
334 | $context[$type] = parse_bbc($context[$type]); |
||||
335 | } |
||||
336 | else |
||||
337 | $context[$type] = nl2br($context[$type]); |
||||
338 | |||||
339 | continue; |
||||
340 | } |
||||
341 | // Don't show redirects. |
||||
342 | elseif ($action['type'] == 'redirect') |
||||
343 | continue; |
||||
344 | elseif ($action['type'] == 'error') |
||||
345 | { |
||||
346 | $context['has_failure'] = true; |
||||
347 | if (isset($action['error_msg']) && isset($action['error_var'])) |
||||
348 | $context['failure_details'] = sprintf($txt['package_will_fail_' . $action['error_msg']], $action['error_var']); |
||||
349 | elseif (isset($action['error_msg'])) |
||||
350 | $context['failure_details'] = isset($txt['package_will_fail_' . $action['error_msg']]) ? $txt['package_will_fail_' . $action['error_msg']] : $action['error_msg']; |
||||
351 | } |
||||
352 | elseif ($action['type'] == 'modification') |
||||
353 | { |
||||
354 | if (!file_exists($packagesdir . '/temp/' . $context['base_path'] . $action['filename'])) |
||||
355 | { |
||||
356 | $context['has_failure'] = true; |
||||
357 | |||||
358 | $context['actions'][] = array( |
||||
359 | 'type' => $txt['execute_modification'], |
||||
360 | 'action' => $smcFunc['htmlspecialchars'](strtr($action['filename'], array($boarddir => '.'))), |
||||
361 | 'description' => $txt['package_action_missing'], |
||||
362 | 'failed' => true, |
||||
363 | ); |
||||
364 | } |
||||
365 | else |
||||
366 | { |
||||
367 | if ($action['boardmod']) |
||||
368 | $mod_actions = parseBoardMod(@file_get_contents($packagesdir . '/temp/' . $context['base_path'] . $action['filename']), true, $action['reverse'], $theme_paths); |
||||
369 | else |
||||
370 | $mod_actions = parseModification(@file_get_contents($packagesdir . '/temp/' . $context['base_path'] . $action['filename']), true, $action['reverse'], $theme_paths); |
||||
371 | |||||
372 | if (count($mod_actions) == 1 && isset($mod_actions[0]) && $mod_actions[0]['type'] == 'error' && $mod_actions[0]['filename'] == '-') |
||||
373 | $mod_actions[0]['filename'] = $action['filename']; |
||||
374 | |||||
375 | foreach ($mod_actions as $key => $mod_action) |
||||
376 | { |
||||
377 | // Lets get the last section of the file name. |
||||
378 | if (isset($mod_action['filename']) && substr($mod_action['filename'], -13) != '.template.php') |
||||
379 | $actual_filename = strtolower(substr(strrchr($mod_action['filename'], '/'), 1) . '||' . $action['filename']); |
||||
380 | elseif (isset($mod_action['filename']) && preg_match('~([\w]*)/([\w]*)\.template\.php$~', $mod_action['filename'], $matches)) |
||||
381 | $actual_filename = strtolower($matches[1] . '/' . $matches[2] . '.template.php' . '||' . $action['filename']); |
||||
382 | else |
||||
383 | $actual_filename = $key; |
||||
384 | |||||
385 | if ($mod_action['type'] == 'opened') |
||||
386 | $failed = false; |
||||
387 | elseif ($mod_action['type'] == 'failure') |
||||
388 | { |
||||
389 | if (empty($mod_action['is_custom'])) |
||||
390 | $context['has_failure'] = true; |
||||
391 | $failed = true; |
||||
392 | } |
||||
393 | elseif ($mod_action['type'] == 'chmod') |
||||
394 | { |
||||
395 | $chmod_files[] = $mod_action['filename']; |
||||
396 | } |
||||
397 | elseif ($mod_action['type'] == 'saved') |
||||
398 | { |
||||
399 | if (!empty($mod_action['is_custom'])) |
||||
400 | { |
||||
401 | if (!isset($context['theme_actions'][$mod_action['is_custom']])) |
||||
402 | $context['theme_actions'][$mod_action['is_custom']] = array( |
||||
403 | 'name' => $theme_paths[$mod_action['is_custom']]['name'], |
||||
404 | 'actions' => array(), |
||||
405 | 'has_failure' => $failed, |
||||
406 | ); |
||||
407 | else |
||||
408 | $context['theme_actions'][$mod_action['is_custom']]['has_failure'] |= $failed; |
||||
409 | |||||
410 | $context['theme_actions'][$mod_action['is_custom']]['actions'][$actual_filename] = array( |
||||
411 | 'type' => $txt['execute_modification'], |
||||
412 | 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), |
||||
413 | 'description' => $failed ? $txt['package_action_failure'] : $txt['package_action_success'], |
||||
414 | 'failed' => $failed, |
||||
415 | ); |
||||
416 | } |
||||
417 | elseif (!isset($context['actions'][$actual_filename])) |
||||
418 | { |
||||
419 | $context['actions'][$actual_filename] = array( |
||||
420 | 'type' => $txt['execute_modification'], |
||||
421 | 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), |
||||
422 | 'description' => $failed ? $txt['package_action_failure'] : $txt['package_action_success'], |
||||
423 | 'failed' => $failed, |
||||
424 | ); |
||||
425 | } |
||||
426 | else |
||||
427 | { |
||||
428 | $context['actions'][$actual_filename]['failed'] |= $failed; |
||||
429 | $context['actions'][$actual_filename]['description'] = $context['actions'][$actual_filename]['failed'] ? $txt['package_action_failure'] : $txt['package_action_success']; |
||||
430 | } |
||||
431 | } |
||||
432 | elseif ($mod_action['type'] == 'skipping') |
||||
433 | { |
||||
434 | $context['actions'][$actual_filename] = array( |
||||
435 | 'type' => $txt['execute_modification'], |
||||
436 | 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), |
||||
437 | 'description' => $txt['package_action_skipping'] |
||||
438 | ); |
||||
439 | } |
||||
440 | elseif ($mod_action['type'] == 'missing' && empty($mod_action['is_custom'])) |
||||
441 | { |
||||
442 | $context['has_failure'] = true; |
||||
443 | $context['actions'][$actual_filename] = array( |
||||
444 | 'type' => $txt['execute_modification'], |
||||
445 | 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), |
||||
446 | 'description' => $txt['package_action_missing'], |
||||
447 | 'failed' => true, |
||||
448 | ); |
||||
449 | } |
||||
450 | elseif ($mod_action['type'] == 'error') |
||||
451 | $context['actions'][$actual_filename] = array( |
||||
452 | 'type' => $txt['execute_modification'], |
||||
453 | 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), |
||||
454 | 'description' => $txt['package_action_error'], |
||||
455 | 'failed' => true, |
||||
456 | ); |
||||
457 | } |
||||
458 | |||||
459 | // We need to loop again just to get the operations down correctly. |
||||
460 | foreach ($mod_actions as $operation_key => $mod_action) |
||||
461 | { |
||||
462 | // Lets get the last section of the file name. |
||||
463 | if (isset($mod_action['filename']) && substr($mod_action['filename'], -13) != '.template.php') |
||||
464 | $actual_filename = strtolower(substr(strrchr($mod_action['filename'], '/'), 1) . '||' . $action['filename']); |
||||
465 | elseif (isset($mod_action['filename']) && preg_match('~([\w]*)/([\w]*)\.template\.php$~', $mod_action['filename'], $matches)) |
||||
466 | $actual_filename = strtolower($matches[1] . '/' . $matches[2] . '.template.php' . '||' . $action['filename']); |
||||
467 | else |
||||
468 | $actual_filename = $key; |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
469 | |||||
470 | // We just need it for actual parse changes. |
||||
471 | if (!in_array($mod_action['type'], array('error', 'result', 'opened', 'saved', 'end', 'missing', 'skipping', 'chmod'))) |
||||
472 | { |
||||
473 | if (empty($mod_action['is_custom'])) |
||||
474 | $context['actions'][$actual_filename]['operations'][] = array( |
||||
475 | 'type' => $txt['execute_modification'], |
||||
476 | 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), |
||||
477 | 'description' => $mod_action['failed'] ? $txt['package_action_failure'] : $txt['package_action_success'], |
||||
478 | 'position' => $mod_action['position'], |
||||
479 | 'operation_key' => $operation_key, |
||||
480 | 'filename' => $action['filename'], |
||||
481 | 'is_boardmod' => $action['boardmod'], |
||||
482 | 'failed' => $mod_action['failed'], |
||||
483 | 'ignore_failure' => !empty($mod_action['ignore_failure']), |
||||
484 | ); |
||||
485 | |||||
486 | // Themes are under the saved type. |
||||
487 | if (isset($mod_action['is_custom']) && isset($context['theme_actions'][$mod_action['is_custom']])) |
||||
488 | $context['theme_actions'][$mod_action['is_custom']]['actions'][$actual_filename]['operations'][] = array( |
||||
489 | 'type' => $txt['execute_modification'], |
||||
490 | 'action' => $smcFunc['htmlspecialchars'](strtr($mod_action['filename'], array($boarddir => '.'))), |
||||
491 | 'description' => $mod_action['failed'] ? $txt['package_action_failure'] : $txt['package_action_success'], |
||||
492 | 'position' => $mod_action['position'], |
||||
493 | 'operation_key' => $operation_key, |
||||
494 | 'filename' => $action['filename'], |
||||
495 | 'is_boardmod' => $action['boardmod'], |
||||
496 | 'failed' => $mod_action['failed'], |
||||
497 | 'ignore_failure' => !empty($mod_action['ignore_failure']), |
||||
498 | ); |
||||
499 | } |
||||
500 | } |
||||
501 | } |
||||
502 | } |
||||
503 | elseif ($action['type'] == 'code') |
||||
504 | { |
||||
505 | $thisAction = array( |
||||
506 | 'type' => $txt['execute_code'], |
||||
507 | 'action' => $smcFunc['htmlspecialchars']($action['filename']), |
||||
508 | ); |
||||
509 | } |
||||
510 | elseif ($action['type'] == 'database' && !$context['uninstalling']) |
||||
511 | { |
||||
512 | $thisAction = array( |
||||
513 | 'type' => $txt['execute_database_changes'], |
||||
514 | 'action' => $smcFunc['htmlspecialchars']($action['filename']), |
||||
515 | ); |
||||
516 | } |
||||
517 | elseif (in_array($action['type'], array('create-dir', 'create-file'))) |
||||
518 | { |
||||
519 | $thisAction = array( |
||||
520 | 'type' => $txt['package_create'] . ' ' . ($action['type'] == 'create-dir' ? $txt['package_tree'] : $txt['package_file']), |
||||
521 | 'action' => $smcFunc['htmlspecialchars'](strtr($action['destination'], array($boarddir => '.'))) |
||||
522 | ); |
||||
523 | } |
||||
524 | elseif ($action['type'] == 'hook') |
||||
525 | { |
||||
526 | $action['description'] = !isset($action['hook'], $action['function']) ? $txt['package_action_failure'] : $txt['package_action_success']; |
||||
527 | |||||
528 | if (!isset($action['hook'], $action['function'])) |
||||
529 | $context['has_failure'] = true; |
||||
530 | |||||
531 | $thisAction = array( |
||||
532 | 'type' => $action['reverse'] ? $txt['execute_hook_remove'] : $txt['execute_hook_add'], |
||||
533 | 'action' => sprintf($txt['execute_hook_action' . ($action['reverse'] ? '_inverse' : '')], $smcFunc['htmlspecialchars']($action['hook'])), |
||||
534 | ); |
||||
535 | } |
||||
536 | elseif ($action['type'] == 'credits') |
||||
537 | { |
||||
538 | $thisAction = array( |
||||
539 | 'type' => $txt['execute_credits_add'], |
||||
540 | 'action' => sprintf($txt['execute_credits_action'], $smcFunc['htmlspecialchars']($action['title'])), |
||||
541 | ); |
||||
542 | } |
||||
543 | elseif ($action['type'] == 'requires') |
||||
544 | { |
||||
545 | $installed = false; |
||||
546 | $version = true; |
||||
547 | |||||
548 | // package missing required values? |
||||
549 | if (!isset($action['id'])) |
||||
550 | $context['has_failure'] = true; |
||||
551 | else |
||||
552 | { |
||||
553 | // See if this dependancy is installed |
||||
554 | $request = $smcFunc['db_query']('', ' |
||||
555 | SELECT version |
||||
556 | FROM {db_prefix}log_packages |
||||
557 | WHERE package_id = {string:current_package} |
||||
558 | AND install_state != {int:not_installed} |
||||
559 | ORDER BY time_installed DESC |
||||
560 | LIMIT 1', |
||||
561 | array( |
||||
562 | 'not_installed' => 0, |
||||
563 | 'current_package' => $action['id'], |
||||
564 | ) |
||||
565 | ); |
||||
566 | $installed = ($smcFunc['db_num_rows']($request) !== 0); |
||||
567 | if ($installed) |
||||
568 | list ($version) = $smcFunc['db_fetch_row']($request); |
||||
569 | $smcFunc['db_free_result']($request); |
||||
570 | |||||
571 | // do a version level check (if requested) in the most basic way |
||||
572 | $version = (isset($action['version']) ? $version == $action['version'] : true); |
||||
573 | } |
||||
574 | |||||
575 | // Set success or failure information |
||||
576 | $action['description'] = ($installed && $version) ? $txt['package_action_success'] : $txt['package_action_failure']; |
||||
577 | $context['has_failure'] = !($installed && $version); |
||||
578 | |||||
579 | $thisAction = array( |
||||
580 | 'type' => $txt['package_requires'], |
||||
581 | 'action' => $txt['package_check_for'] . ' ' . $action['id'] . (isset($action['version']) ? (' / ' . ($version ? $action['version'] : '<span class="error">' . $action['version'] . '</span>')) : ''), |
||||
582 | ); |
||||
583 | } |
||||
584 | elseif (in_array($action['type'], array('require-dir', 'require-file'))) |
||||
585 | { |
||||
586 | // Do this one... |
||||
587 | $thisAction = array( |
||||
588 | 'type' => $txt['package_extract'] . ' ' . ($action['type'] == 'require-dir' ? $txt['package_tree'] : $txt['package_file']), |
||||
589 | 'action' => $smcFunc['htmlspecialchars'](strtr($action['destination'], array($boarddir => '.'))) |
||||
590 | ); |
||||
591 | |||||
592 | // Could this be theme related? |
||||
593 | if (!empty($action['unparsed_destination']) && preg_match('~^\$(languagedir|languages_dir|imagesdir|themedir|themes_dir)~i', $action['unparsed_destination'], $matches)) |
||||
594 | { |
||||
595 | // Is the action already stated? |
||||
596 | $theme_action = !empty($action['theme_action']) && in_array($action['theme_action'], array('no', 'yes', 'auto')) ? $action['theme_action'] : 'auto'; |
||||
597 | // If it's not auto do we think we have something we can act upon? |
||||
598 | if ($theme_action != 'auto' && !in_array($matches[1], array('languagedir', 'languages_dir', 'imagesdir', 'themedir'))) |
||||
599 | $theme_action = ''; |
||||
600 | // ... or if it's auto do we even want to do anything? |
||||
601 | elseif ($theme_action == 'auto' && $matches[1] != 'imagesdir') |
||||
602 | $theme_action = ''; |
||||
603 | |||||
604 | // So, we still want to do something? |
||||
605 | if ($theme_action != '') |
||||
606 | $themeFinds['candidates'][] = $action; |
||||
607 | // Otherwise is this is going into another theme record it. |
||||
608 | elseif ($matches[1] == 'themes_dir') |
||||
609 | $themeFinds['other_themes'][] = strtolower(strtr(parse_path($action['unparsed_destination']), array('\\' => '/')) . '/' . basename($action['filename'])); |
||||
610 | } |
||||
611 | } |
||||
612 | elseif (in_array($action['type'], array('move-dir', 'move-file'))) |
||||
613 | $thisAction = array( |
||||
614 | 'type' => $txt['package_move'] . ' ' . ($action['type'] == 'move-dir' ? $txt['package_tree'] : $txt['package_file']), |
||||
615 | 'action' => $smcFunc['htmlspecialchars'](strtr($action['source'], array($boarddir => '.'))) . ' => ' . $smcFunc['htmlspecialchars'](strtr($action['destination'], array($boarddir => '.'))) |
||||
616 | ); |
||||
617 | elseif (in_array($action['type'], array('remove-dir', 'remove-file'))) |
||||
618 | { |
||||
619 | $thisAction = array( |
||||
620 | 'type' => $txt['package_delete'] . ' ' . ($action['type'] == 'remove-dir' ? $txt['package_tree'] : $txt['package_file']), |
||||
621 | 'action' => $smcFunc['htmlspecialchars'](strtr($action['filename'], array($boarddir => '.'))) |
||||
622 | ); |
||||
623 | |||||
624 | // Could this be theme related? |
||||
625 | if (!empty($action['unparsed_filename']) && preg_match('~^\$(languagedir|languages_dir|imagesdir|themedir|themes_dir)~i', $action['unparsed_filename'], $matches)) |
||||
626 | { |
||||
627 | // Is the action already stated? |
||||
628 | $theme_action = !empty($action['theme_action']) && in_array($action['theme_action'], array('no', 'yes', 'auto')) ? $action['theme_action'] : 'auto'; |
||||
629 | $action['unparsed_destination'] = $action['unparsed_filename']; |
||||
630 | |||||
631 | // If it's not auto do we think we have something we can act upon? |
||||
632 | if ($theme_action != 'auto' && !in_array($matches[1], array('languagedir', 'languages_dir', 'imagesdir', 'themedir'))) |
||||
633 | $theme_action = ''; |
||||
634 | // ... or if it's auto do we even want to do anything? |
||||
635 | elseif ($theme_action == 'auto' && $matches[1] != 'imagesdir') |
||||
636 | $theme_action = ''; |
||||
637 | |||||
638 | // So, we still want to do something? |
||||
639 | if ($theme_action != '') |
||||
640 | $themeFinds['candidates'][] = $action; |
||||
641 | // Otherwise is this is going into another theme record it. |
||||
642 | elseif ($matches[1] == 'themes_dir') |
||||
643 | $themeFinds['other_themes'][] = strtolower(strtr(parse_path($action['unparsed_filename']), array('\\' => '/')) . '/' . basename($action['filename'])); |
||||
644 | } |
||||
645 | } |
||||
646 | |||||
647 | if (empty($thisAction)) |
||||
648 | continue; |
||||
649 | |||||
650 | if (!in_array($action['type'], array('hook', 'credits'))) |
||||
651 | { |
||||
652 | if ($context['uninstalling']) |
||||
653 | $file = in_array($action['type'], array('remove-dir', 'remove-file')) ? $action['filename'] : $packagesdir . '/temp/' . $context['base_path'] . $action['filename']; |
||||
654 | else |
||||
655 | $file = $packagesdir . '/temp/' . $context['base_path'] . $action['filename']; |
||||
656 | } |
||||
657 | |||||
658 | // Don't fail if a file/directory we're trying to create doesn't exist... |
||||
659 | if (isset($action['filename']) && !file_exists($file) && !in_array($action['type'], array('create-dir', 'create-file'))) |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
660 | { |
||||
661 | $context['has_failure'] = true; |
||||
662 | |||||
663 | $thisAction += array( |
||||
664 | 'description' => $txt['package_action_missing'], |
||||
665 | 'failed' => true, |
||||
666 | ); |
||||
667 | } |
||||
668 | |||||
669 | // @todo None given? |
||||
670 | if (empty($thisAction['description'])) |
||||
671 | $thisAction['description'] = isset($action['description']) ? $action['description'] : ''; |
||||
672 | |||||
673 | $context['actions'][] = $thisAction; |
||||
674 | } |
||||
675 | |||||
676 | // Have we got some things which we might want to do "multi-theme"? |
||||
677 | if (!empty($themeFinds['candidates'])) |
||||
678 | { |
||||
679 | foreach ($themeFinds['candidates'] as $action_data) |
||||
680 | { |
||||
681 | // Get the part of the file we'll be dealing with. |
||||
682 | preg_match('~^\$(languagedir|languages_dir|imagesdir|themedir)(\\|/)*(.+)*~i', $action_data['unparsed_destination'], $matches); |
||||
683 | |||||
684 | if ($matches[1] == 'imagesdir') |
||||
685 | $path = '/' . basename($settings['default_images_url']); |
||||
686 | elseif ($matches[1] == 'languagedir' || $matches[1] == 'languages_dir') |
||||
687 | $path = '/languages'; |
||||
688 | else |
||||
689 | $path = ''; |
||||
690 | |||||
691 | if (!empty($matches[3])) |
||||
692 | $path .= $matches[3]; |
||||
693 | |||||
694 | if (!$context['uninstalling']) |
||||
695 | $path .= '/' . basename($action_data['filename']); |
||||
696 | |||||
697 | // Loop through each custom theme to note it's candidacy! |
||||
698 | foreach ($theme_paths as $id => $theme_data) |
||||
699 | { |
||||
700 | if (isset($theme_data['theme_dir']) && $id != 1) |
||||
701 | { |
||||
702 | $real_path = $theme_data['theme_dir'] . $path; |
||||
703 | |||||
704 | // Confirm that we don't already have this dealt with by another entry. |
||||
705 | if (!in_array(strtolower(strtr($real_path, array('\\' => '/'))), $themeFinds['other_themes'])) |
||||
706 | { |
||||
707 | // Check if we will need to chmod this. |
||||
708 | if (!mktree(dirname($real_path), false)) |
||||
0 ignored issues
–
show
false of type false is incompatible with the type integer expected by parameter $mode of mktree() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
709 | { |
||||
710 | $temp = dirname($real_path); |
||||
711 | while (!file_exists($temp) && strlen($temp) > 1) |
||||
712 | $temp = dirname($temp); |
||||
713 | $chmod_files[] = $temp; |
||||
714 | } |
||||
715 | |||||
716 | if ($action_data['type'] == 'require-dir' && !is_writable($real_path) && (file_exists($real_path) || !is_writable(dirname($real_path)))) |
||||
717 | $chmod_files[] = $real_path; |
||||
718 | |||||
719 | if (!isset($context['theme_actions'][$id])) |
||||
720 | $context['theme_actions'][$id] = array( |
||||
721 | 'name' => $theme_data['name'], |
||||
722 | 'actions' => array(), |
||||
723 | ); |
||||
724 | |||||
725 | if ($context['uninstalling']) |
||||
726 | $context['theme_actions'][$id]['actions'][] = array( |
||||
727 | 'type' => $txt['package_delete'] . ' ' . ($action_data['type'] == 'require-dir' ? $txt['package_tree'] : $txt['package_file']), |
||||
728 | 'action' => strtr($real_path, array('\\' => '/', $boarddir => '.')), |
||||
729 | 'description' => '', |
||||
730 | 'value' => base64_encode($smcFunc['json_encode'](array('type' => $action_data['type'], 'orig' => $action_data['filename'], 'future' => $real_path, 'id' => $id))), |
||||
731 | 'not_mod' => true, |
||||
732 | ); |
||||
733 | else |
||||
734 | $context['theme_actions'][$id]['actions'][] = array( |
||||
735 | 'type' => $txt['package_extract'] . ' ' . ($action_data['type'] == 'require-dir' ? $txt['package_tree'] : $txt['package_file']), |
||||
736 | 'action' => strtr($real_path, array('\\' => '/', $boarddir => '.')), |
||||
737 | 'description' => '', |
||||
738 | 'value' => base64_encode($smcFunc['json_encode'](array('type' => $action_data['type'], 'orig' => $action_data['destination'], 'future' => $real_path, 'id' => $id))), |
||||
739 | 'not_mod' => true, |
||||
740 | ); |
||||
741 | } |
||||
742 | } |
||||
743 | } |
||||
744 | } |
||||
745 | } |
||||
746 | |||||
747 | // Trash the cache... which will also check permissions for us! |
||||
748 | package_flush_cache(true); |
||||
749 | |||||
750 | if (file_exists($packagesdir . '/temp')) |
||||
751 | deltree($packagesdir . '/temp'); |
||||
752 | |||||
753 | if (!empty($chmod_files)) |
||||
754 | { |
||||
755 | $ftp_status = create_chmod_control($chmod_files); |
||||
756 | $context['ftp_needed'] = !empty($ftp_status['files']['notwritable']) && !empty($context['package_ftp']); |
||||
757 | } |
||||
758 | |||||
759 | $context['post_url'] = $scripturl . '?action=admin;area=packages;sa=' . ($context['uninstalling'] ? 'uninstall' : 'install') . ($context['ftp_needed'] ? '' : '2') . ';package=' . $context['filename'] . ';pid=' . $context['install_id']; |
||||
760 | checkSubmitOnce('register'); |
||||
761 | } |
||||
762 | |||||
763 | /** |
||||
764 | * Apply another type of (avatar, language, etc.) package. |
||||
765 | */ |
||||
766 | function PackageInstall() |
||||
767 | { |
||||
768 | global $txt, $context, $boardurl, $scripturl, $sourcedir, $packagesdir, $modSettings; |
||||
769 | global $user_info, $smcFunc; |
||||
770 | |||||
771 | // Make sure we don't install this mod twice. |
||||
772 | checkSubmitOnce('check'); |
||||
773 | checkSession(); |
||||
774 | |||||
775 | // If there's no file, what are we installing? |
||||
776 | if (!isset($_REQUEST['package']) || $_REQUEST['package'] == '') |
||||
777 | redirectexit('action=admin;area=packages'); |
||||
778 | $context['filename'] = $_REQUEST['package']; |
||||
779 | |||||
780 | // If this is an uninstall, we'll have an id. |
||||
781 | $context['install_id'] = isset($_REQUEST['pid']) ? (int) $_REQUEST['pid'] : 0; |
||||
782 | |||||
783 | require_once($sourcedir . '/Subs-Package.php'); |
||||
784 | |||||
785 | // @todo Perhaps do it in steps, if necessary? |
||||
786 | |||||
787 | $context['uninstalling'] = $_REQUEST['sa'] == 'uninstall2'; |
||||
788 | |||||
789 | // Set up the linktree for other. |
||||
790 | $context['linktree'][count($context['linktree']) - 1] = array( |
||||
791 | 'url' => $scripturl . '?action=admin;area=packages;sa=browse', |
||||
792 | 'name' => $context['uninstalling'] ? $txt['uninstall'] : $txt['extracting'] |
||||
793 | ); |
||||
794 | $context['page_title'] .= ' - ' . ($context['uninstalling'] ? $txt['uninstall'] : $txt['extracting']); |
||||
795 | |||||
796 | $context['sub_template'] = 'extract_package'; |
||||
797 | |||||
798 | if (!file_exists($packagesdir . '/' . $context['filename'])) |
||||
799 | fatal_lang_error('package_no_file', false); |
||||
800 | |||||
801 | // Load up the package FTP information? |
||||
802 | create_chmod_control(array(), array('destination_url' => $scripturl . '?action=admin;area=packages;sa=' . $_REQUEST['sa'] . ';package=' . $_REQUEST['package'])); |
||||
803 | |||||
804 | // Make sure temp directory exists and is empty! |
||||
805 | if (file_exists($packagesdir . '/temp')) |
||||
806 | deltree($packagesdir . '/temp', false); |
||||
807 | else |
||||
808 | mktree($packagesdir . '/temp', 0777); |
||||
809 | |||||
810 | // Let the unpacker do the work. |
||||
811 | if (is_file($packagesdir . '/' . $context['filename'])) |
||||
812 | { |
||||
813 | $context['extracted_files'] = read_tgz_file($packagesdir . '/' . $context['filename'], $packagesdir . '/temp'); |
||||
814 | |||||
815 | if (!file_exists($packagesdir . '/temp/package-info.xml')) |
||||
816 | foreach ($context['extracted_files'] as $file) |
||||
817 | if (basename($file['filename']) == 'package-info.xml') |
||||
818 | { |
||||
819 | $context['base_path'] = dirname($file['filename']) . '/'; |
||||
820 | break; |
||||
821 | } |
||||
822 | |||||
823 | if (!isset($context['base_path'])) |
||||
824 | $context['base_path'] = ''; |
||||
825 | } |
||||
826 | elseif (is_dir($packagesdir . '/' . $context['filename'])) |
||||
827 | { |
||||
828 | copytree($packagesdir . '/' . $context['filename'], $packagesdir . '/temp'); |
||||
829 | $context['extracted_files'] = listtree($packagesdir . '/temp'); |
||||
830 | $context['base_path'] = ''; |
||||
831 | } |
||||
832 | else |
||||
833 | fatal_lang_error('no_access', false); |
||||
834 | |||||
835 | // Are we installing this into any custom themes? |
||||
836 | $custom_themes = array(1); |
||||
837 | $known_themes = explode(',', $modSettings['knownThemes']); |
||||
838 | if (!empty($_POST['custom_theme'])) |
||||
839 | { |
||||
840 | foreach ($_POST['custom_theme'] as $tid) |
||||
841 | if (in_array($tid, $known_themes)) |
||||
842 | $custom_themes[] = (int) $tid; |
||||
843 | } |
||||
844 | |||||
845 | // Now load up the paths of the themes that we need to know about. |
||||
846 | $request = $smcFunc['db_query']('', ' |
||||
847 | SELECT id_theme, variable, value |
||||
848 | FROM {db_prefix}themes |
||||
849 | WHERE id_theme IN ({array_int:custom_themes}) |
||||
850 | AND variable IN ({string:name}, {string:theme_dir})', |
||||
851 | array( |
||||
852 | 'custom_themes' => $custom_themes, |
||||
853 | 'name' => 'name', |
||||
854 | 'theme_dir' => 'theme_dir', |
||||
855 | ) |
||||
856 | ); |
||||
857 | $theme_paths = array(); |
||||
858 | $themes_installed = array(1); |
||||
859 | |||||
860 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||||
861 | $theme_paths[$row['id_theme']][$row['variable']] = $row['value']; |
||||
862 | |||||
863 | $smcFunc['db_free_result']($request); |
||||
864 | |||||
865 | // Are there any theme copying that we want to take place? |
||||
866 | $context['theme_copies'] = array( |
||||
867 | 'require-file' => array(), |
||||
868 | 'require-dir' => array(), |
||||
869 | ); |
||||
870 | if (!empty($_POST['theme_changes'])) |
||||
871 | { |
||||
872 | foreach ($_POST['theme_changes'] as $change) |
||||
873 | { |
||||
874 | if (empty($change)) |
||||
875 | continue; |
||||
876 | $theme_data = $smcFunc['json_decode'](base64_decode($change), true); |
||||
877 | if (empty($theme_data['type'])) |
||||
878 | continue; |
||||
879 | |||||
880 | $themes_installed[] = $theme_data['id']; |
||||
881 | $context['theme_copies'][$theme_data['type']][$theme_data['orig']][] = $theme_data['future']; |
||||
882 | } |
||||
883 | } |
||||
884 | |||||
885 | // Get the package info... |
||||
886 | $packageInfo = getPackageInfo($context['filename']); |
||||
887 | if (!is_array($packageInfo)) |
||||
888 | fatal_lang_error($packageInfo); |
||||
889 | |||||
890 | $packageInfo['filename'] = $context['filename']; |
||||
891 | |||||
892 | // Set the type of extraction... |
||||
893 | $context['extract_type'] = isset($packageInfo['type']) ? $packageInfo['type'] : 'modification'; |
||||
894 | |||||
895 | // Create a backup file to roll back to! (but if they do this more than once, don't run it a zillion times.) |
||||
896 | if (!empty($modSettings['package_make_full_backups']) && (!isset($_SESSION['last_backup_for']) || $_SESSION['last_backup_for'] != $context['filename'] . ($context['uninstalling'] ? '$$' : '$'))) |
||||
897 | { |
||||
898 | $_SESSION['last_backup_for'] = $context['filename'] . ($context['uninstalling'] ? '$$' : '$'); |
||||
899 | $result = package_create_backup(($context['uninstalling'] ? 'backup_' : 'before_') . strtok($context['filename'], '.')); |
||||
900 | if (!$result) |
||||
901 | fatal_lang_error('could_not_package_backup', false); |
||||
902 | } |
||||
903 | |||||
904 | // The mod isn't installed.... unless proven otherwise. |
||||
905 | $context['is_installed'] = false; |
||||
906 | |||||
907 | // Is it actually installed? |
||||
908 | $request = $smcFunc['db_query']('', ' |
||||
909 | SELECT version, themes_installed, db_changes |
||||
910 | FROM {db_prefix}log_packages |
||||
911 | WHERE package_id = {string:current_package} |
||||
912 | AND install_state != {int:not_installed} |
||||
913 | ORDER BY time_installed DESC |
||||
914 | LIMIT 1', |
||||
915 | array( |
||||
916 | 'not_installed' => 0, |
||||
917 | 'current_package' => $packageInfo['id'], |
||||
918 | ) |
||||
919 | ); |
||||
920 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||||
921 | { |
||||
922 | $old_themes = explode(',', $row['themes_installed']); |
||||
923 | $old_version = $row['version']; |
||||
924 | $db_changes = empty($row['db_changes']) ? array() : $smcFunc['json_decode']($row['db_changes'], true); |
||||
925 | } |
||||
926 | $smcFunc['db_free_result']($request); |
||||
927 | |||||
928 | // Wait, it's not installed yet! |
||||
929 | // @todo Replace with a better error message! |
||||
930 | if (!isset($old_version) && $context['uninstalling']) |
||||
931 | { |
||||
932 | deltree($packagesdir . '/temp'); |
||||
933 | fatal_error('Hacker?', false); |
||||
934 | } |
||||
935 | // Uninstalling? |
||||
936 | elseif ($context['uninstalling']) |
||||
937 | { |
||||
938 | $install_log = parsePackageInfo($packageInfo['xml'], false, 'uninstall'); |
||||
939 | |||||
940 | // Gadzooks! There's no uninstaller at all!? |
||||
941 | if (empty($install_log)) |
||||
942 | fatal_lang_error('package_uninstall_cannot', false); |
||||
943 | |||||
944 | // They can only uninstall from what it was originally installed into. |
||||
945 | foreach ($theme_paths as $id => $data) |
||||
946 | if ($id != 1 && !in_array($id, $old_themes)) |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
947 | unset($theme_paths[$id]); |
||||
948 | } |
||||
949 | elseif (isset($old_version) && $old_version != $packageInfo['version']) |
||||
950 | { |
||||
951 | // Look for an upgrade... |
||||
952 | $install_log = parsePackageInfo($packageInfo['xml'], false, 'upgrade', $old_version); |
||||
953 | |||||
954 | // There was no upgrade.... |
||||
955 | if (empty($install_log)) |
||||
956 | $context['is_installed'] = true; |
||||
957 | else |
||||
958 | { |
||||
959 | // Upgrade previous themes only! |
||||
960 | foreach ($theme_paths as $id => $data) |
||||
961 | if ($id != 1 && !in_array($id, $old_themes)) |
||||
962 | unset($theme_paths[$id]); |
||||
963 | } |
||||
964 | } |
||||
965 | elseif (isset($old_version) && $old_version == $packageInfo['version']) |
||||
966 | $context['is_installed'] = true; |
||||
967 | |||||
968 | if (!isset($old_version) || $context['is_installed']) |
||||
969 | $install_log = parsePackageInfo($packageInfo['xml'], false, 'install'); |
||||
970 | |||||
971 | $context['install_finished'] = false; |
||||
972 | |||||
973 | // @todo Make a log of any errors that occurred and output them? |
||||
974 | |||||
975 | if (!empty($install_log)) |
||||
976 | { |
||||
977 | $failed_steps = array(); |
||||
978 | $failed_count = 0; |
||||
979 | |||||
980 | foreach ($install_log as $action) |
||||
981 | { |
||||
982 | $failed_count++; |
||||
983 | |||||
984 | if ($action['type'] == 'modification' && !empty($action['filename'])) |
||||
985 | { |
||||
986 | if ($action['boardmod']) |
||||
987 | $mod_actions = parseBoardMod(file_get_contents($packagesdir . '/temp/' . $context['base_path'] . $action['filename']), false, $action['reverse'], $theme_paths); |
||||
988 | else |
||||
989 | $mod_actions = parseModification(file_get_contents($packagesdir . '/temp/' . $context['base_path'] . $action['filename']), false, $action['reverse'], $theme_paths); |
||||
990 | |||||
991 | // Any errors worth noting? |
||||
992 | foreach ($mod_actions as $key => $modAction) |
||||
993 | { |
||||
994 | if ($modAction['type'] == 'failure') |
||||
995 | $failed_steps[] = array( |
||||
996 | 'file' => $modAction['filename'], |
||||
997 | 'large_step' => $failed_count, |
||||
998 | 'sub_step' => $key, |
||||
999 | 'theme' => 1, |
||||
1000 | ); |
||||
1001 | |||||
1002 | // Gather the themes we installed into. |
||||
1003 | if (!empty($modAction['is_custom'])) |
||||
1004 | $themes_installed[] = $modAction['is_custom']; |
||||
1005 | } |
||||
1006 | } |
||||
1007 | elseif ($action['type'] == 'code' && !empty($action['filename'])) |
||||
1008 | { |
||||
1009 | // This is just here as reference for what is available. |
||||
1010 | global $txt, $boarddir, $sourcedir, $modSettings, $context, $settings, $smcFunc; |
||||
1011 | |||||
1012 | // Now include the file and be done with it ;). |
||||
1013 | if (file_exists($packagesdir . '/temp/' . $context['base_path'] . $action['filename'])) |
||||
1014 | require($packagesdir . '/temp/' . $context['base_path'] . $action['filename']); |
||||
1015 | } |
||||
1016 | elseif ($action['type'] == 'credits') |
||||
1017 | { |
||||
1018 | // Time to build the billboard |
||||
1019 | $credits_tag = array( |
||||
1020 | 'url' => $action['url'], |
||||
1021 | 'license' => $action['license'], |
||||
1022 | 'licenseurl' => $action['licenseurl'], |
||||
1023 | 'copyright' => $action['copyright'], |
||||
1024 | 'title' => $action['title'], |
||||
1025 | ); |
||||
1026 | } |
||||
1027 | elseif ($action['type'] == 'hook' && isset($action['hook'], $action['function'])) |
||||
1028 | { |
||||
1029 | // Set the system to ignore hooks, but only if it wasn't changed before. |
||||
1030 | if (!isset($context['ignore_hook_errors'])) |
||||
1031 | $context['ignore_hook_errors'] = true; |
||||
1032 | |||||
1033 | if ($action['reverse']) |
||||
1034 | remove_integration_function($action['hook'], $action['function'], true, $action['include_file'], $action['object']); |
||||
1035 | else |
||||
1036 | add_integration_function($action['hook'], $action['function'], true, $action['include_file'], $action['object']); |
||||
1037 | } |
||||
1038 | // Only do the database changes on uninstall if requested. |
||||
1039 | elseif ($action['type'] == 'database' && !empty($action['filename']) && (!$context['uninstalling'] || !empty($_POST['do_db_changes']))) |
||||
1040 | { |
||||
1041 | // These can also be there for database changes. |
||||
1042 | global $txt, $boarddir, $sourcedir, $modSettings, $context, $settings, $smcFunc; |
||||
1043 | global $db_package_log; |
||||
1044 | |||||
1045 | // We'll likely want the package specific database functionality! |
||||
1046 | db_extend('packages'); |
||||
1047 | |||||
1048 | // Let the file work its magic ;) |
||||
1049 | if (file_exists($packagesdir . '/temp/' . $context['base_path'] . $action['filename'])) |
||||
1050 | require($packagesdir . '/temp/' . $context['base_path'] . $action['filename']); |
||||
1051 | } |
||||
1052 | // Handle a redirect... |
||||
1053 | elseif ($action['type'] == 'redirect' && !empty($action['redirect_url'])) |
||||
1054 | { |
||||
1055 | $context['redirect_url'] = $action['redirect_url']; |
||||
1056 | $context['redirect_text'] = !empty($action['filename']) && file_exists($packagesdir . '/temp/' . $context['base_path'] . $action['filename']) ? $smcFunc['htmlspecialchars'](file_get_contents($packagesdir . '/temp/' . $context['base_path'] . $action['filename'])) : ($context['uninstalling'] ? $txt['package_uninstall_done'] : $txt['package_installed_done']); |
||||
1057 | $context['redirect_timeout'] = empty($action['redirect_timeout']) ? 5 : (int) ceil($action['redirect_timeout'] / 1000); |
||||
1058 | if (!empty($action['parse_bbc'])) |
||||
1059 | { |
||||
1060 | require_once($sourcedir . '/Subs-Post.php'); |
||||
1061 | $context['redirect_text'] = preg_replace('~\[[/]?html\]~i', '', $context['redirect_text']); |
||||
1062 | preparsecode($context['redirect_text']); |
||||
1063 | $context['redirect_text'] = parse_bbc($context['redirect_text']); |
||||
1064 | } |
||||
1065 | |||||
1066 | // Parse out a couple of common urls. |
||||
1067 | $urls = array( |
||||
1068 | '$boardurl' => $boardurl, |
||||
1069 | '$scripturl' => $scripturl, |
||||
1070 | '$session_var' => $context['session_var'], |
||||
1071 | '$session_id' => $context['session_id'], |
||||
1072 | ); |
||||
1073 | |||||
1074 | $context['redirect_url'] = strtr($context['redirect_url'], $urls); |
||||
1075 | } |
||||
1076 | } |
||||
1077 | |||||
1078 | package_flush_cache(); |
||||
1079 | |||||
1080 | // See if this is already installed, and change it's state as required. |
||||
1081 | $request = $smcFunc['db_query']('', ' |
||||
1082 | SELECT package_id, install_state, db_changes |
||||
1083 | FROM {db_prefix}log_packages |
||||
1084 | WHERE install_state != {int:not_installed} |
||||
1085 | AND package_id = {string:current_package} |
||||
1086 | ' . ($context['install_id'] ? ' AND id_install = {int:install_id} ' : '') . ' |
||||
1087 | ORDER BY time_installed DESC |
||||
1088 | LIMIT 1', |
||||
1089 | array( |
||||
1090 | 'not_installed' => 0, |
||||
1091 | 'install_id' => $context['install_id'], |
||||
1092 | 'current_package' => $packageInfo['id'], |
||||
1093 | ) |
||||
1094 | ); |
||||
1095 | $is_upgrade = false; |
||||
1096 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||||
1097 | { |
||||
1098 | // Uninstalling? |
||||
1099 | if ($context['uninstalling']) |
||||
1100 | { |
||||
1101 | $smcFunc['db_query']('', ' |
||||
1102 | UPDATE {db_prefix}log_packages |
||||
1103 | SET install_state = {int:not_installed}, member_removed = {string:member_name}, |
||||
1104 | id_member_removed = {int:current_member}, time_removed = {int:current_time} |
||||
1105 | WHERE package_id = {string:package_id} |
||||
1106 | AND id_install = {int:install_id}', |
||||
1107 | array( |
||||
1108 | 'current_member' => $user_info['id'], |
||||
1109 | 'not_installed' => 0, |
||||
1110 | 'current_time' => time(), |
||||
1111 | 'package_id' => $row['package_id'], |
||||
1112 | 'member_name' => $user_info['name'], |
||||
1113 | 'install_id' => $context['install_id'], |
||||
1114 | ) |
||||
1115 | ); |
||||
1116 | } |
||||
1117 | // Otherwise must be an upgrade. |
||||
1118 | else |
||||
1119 | { |
||||
1120 | $is_upgrade = true; |
||||
1121 | $old_db_changes = empty($row['db_changes']) ? array() : $smcFunc['json_decode']($row['db_changes'], true); |
||||
1122 | |||||
1123 | // Mark the old version as uninstalled |
||||
1124 | $smcFunc['db_query']('', ' |
||||
1125 | UPDATE {db_prefix}log_packages |
||||
1126 | SET install_state = {int:not_installed}, member_removed = {string:member_name}, |
||||
1127 | id_member_removed = {int:current_member}, time_removed = {int:current_time} |
||||
1128 | WHERE package_id = {string:package_id} |
||||
1129 | AND version = {string:old_version}', |
||||
1130 | array( |
||||
1131 | 'current_member' => $user_info['id'], |
||||
1132 | 'not_installed' => 0, |
||||
1133 | 'current_time' => time(), |
||||
1134 | 'package_id' => $row['package_id'], |
||||
1135 | 'member_name' => $user_info['name'], |
||||
1136 | 'old_version' => $old_version, |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1137 | ) |
||||
1138 | ); |
||||
1139 | } |
||||
1140 | } |
||||
1141 | |||||
1142 | // Assuming we're not uninstalling, add the entry. |
||||
1143 | if (!$context['uninstalling']) |
||||
1144 | { |
||||
1145 | // Reload the settings table for mods that have altered them upon installation |
||||
1146 | reloadSettings(); |
||||
1147 | |||||
1148 | // Any db changes from older version? |
||||
1149 | if (!empty($old_db_changes)) |
||||
1150 | $db_package_log = empty($db_package_log) ? $old_db_changes : array_merge($old_db_changes, $db_package_log); |
||||
1151 | |||||
1152 | // If there are some database changes we might want to remove then filter them out. |
||||
1153 | if (!empty($db_package_log)) |
||||
1154 | { |
||||
1155 | // We're really just checking for entries which are create table AND add columns (etc). |
||||
1156 | $tables = array(); |
||||
1157 | |||||
1158 | usort($db_package_log, function ($a, $b) |
||||
1159 | { |
||||
1160 | if ($a[0] == $b[0]) |
||||
1161 | return 0; |
||||
1162 | return $a[0] == 'remove_table' ? -1 : 1; |
||||
1163 | }); |
||||
1164 | |||||
1165 | foreach ($db_package_log as $k => $log) |
||||
1166 | { |
||||
1167 | if ($log[0] == 'remove_table') |
||||
1168 | $tables[] = $log[1]; |
||||
1169 | elseif (in_array($log[1], $tables)) |
||||
1170 | unset($db_package_log[$k]); |
||||
1171 | } |
||||
1172 | $db_changes = $smcFunc['json_encode']($db_package_log); |
||||
1173 | } |
||||
1174 | else |
||||
1175 | $db_changes = ''; |
||||
1176 | |||||
1177 | // What themes did we actually install? |
||||
1178 | $themes_installed = array_unique($themes_installed); |
||||
1179 | $themes_installed = implode(',', $themes_installed); |
||||
1180 | |||||
1181 | // What failed steps? |
||||
1182 | $failed_step_insert = $smcFunc['json_encode']($failed_steps); |
||||
1183 | |||||
1184 | // Un-sanitize things before we insert them... |
||||
1185 | $keys = array('filename', 'name', 'id', 'version'); |
||||
1186 | foreach ($keys as $key) |
||||
1187 | { |
||||
1188 | // Yay for variable variables... |
||||
1189 | ${"package_$key"} = un_htmlspecialchars($packageInfo[$key]); |
||||
1190 | } |
||||
1191 | |||||
1192 | // Credits tag? |
||||
1193 | $credits_tag = (empty($credits_tag)) ? '' : $smcFunc['json_encode']($credits_tag); |
||||
1194 | $smcFunc['db_insert']('', |
||||
1195 | '{db_prefix}log_packages', |
||||
1196 | array( |
||||
1197 | 'filename' => 'string', 'name' => 'string', 'package_id' => 'string', 'version' => 'string', |
||||
1198 | 'id_member_installed' => 'int', 'member_installed' => 'string', 'time_installed' => 'int', |
||||
1199 | 'install_state' => 'int', 'failed_steps' => 'string', 'themes_installed' => 'string', |
||||
1200 | 'member_removed' => 'int', 'db_changes' => 'string', 'credits' => 'string', |
||||
1201 | ), |
||||
1202 | array( |
||||
1203 | $package_filename, $package_name, $package_id, $package_version, |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Comprehensibility
Best Practice
introduced
by
Comprehensibility
Best Practice
introduced
by
Comprehensibility
Best Practice
introduced
by
|
|||||
1204 | $user_info['id'], $user_info['name'], time(), |
||||
1205 | $is_upgrade ? 2 : 1, $failed_step_insert, $themes_installed, |
||||
1206 | 0, $db_changes, $credits_tag, |
||||
1207 | ), |
||||
1208 | array('id_install') |
||||
1209 | ); |
||||
1210 | } |
||||
1211 | $smcFunc['db_free_result']($request); |
||||
1212 | |||||
1213 | $context['install_finished'] = true; |
||||
1214 | } |
||||
1215 | |||||
1216 | // If there's database changes - and they want them removed - let's do it last! |
||||
1217 | if (!empty($db_changes) && !empty($_POST['do_db_changes'])) |
||||
1218 | { |
||||
1219 | // We're gonna be needing the package db functions! |
||||
1220 | db_extend('packages'); |
||||
1221 | |||||
1222 | foreach ($db_changes as $change) |
||||
1223 | { |
||||
1224 | if ($change[0] == 'remove_table' && isset($change[1])) |
||||
1225 | $smcFunc['db_drop_table']($change[1]); |
||||
1226 | elseif ($change[0] == 'remove_column' && isset($change[2])) |
||||
1227 | $smcFunc['db_remove_column']($change[1], $change[2]); |
||||
1228 | elseif ($change[0] == 'remove_index' && isset($change[2])) |
||||
1229 | $smcFunc['db_remove_index']($change[1], $change[2]); |
||||
1230 | } |
||||
1231 | } |
||||
1232 | |||||
1233 | // Clean house... get rid of the evidence ;). |
||||
1234 | if (file_exists($packagesdir . '/temp')) |
||||
1235 | deltree($packagesdir . '/temp'); |
||||
1236 | |||||
1237 | // Log what we just did. |
||||
1238 | logAction($context['uninstalling'] ? 'uninstall_package' : (!empty($is_upgrade) ? 'upgrade_package' : 'install_package'), array('package' => $smcFunc['htmlspecialchars']($packageInfo['name']), 'version' => $smcFunc['htmlspecialchars']($packageInfo['version'])), 'admin'); |
||||
1239 | |||||
1240 | // Just in case, let's clear the whole cache and any minimized CSS and JS to avoid anything going up the swanny. |
||||
1241 | clean_cache(); |
||||
1242 | deleteAllMinified(); |
||||
1243 | |||||
1244 | // Restore file permissions? |
||||
1245 | create_chmod_control(array(), array(), true); |
||||
1246 | } |
||||
1247 | |||||
1248 | /** |
||||
1249 | * List the files in a package. |
||||
1250 | */ |
||||
1251 | function PackageList() |
||||
1252 | { |
||||
1253 | global $txt, $scripturl, $context, $sourcedir, $packagesdir; |
||||
1254 | |||||
1255 | require_once($sourcedir . '/Subs-Package.php'); |
||||
1256 | |||||
1257 | // No package? Show him or her the door. |
||||
1258 | if (!isset($_REQUEST['package']) || $_REQUEST['package'] == '') |
||||
1259 | redirectexit('action=admin;area=packages'); |
||||
1260 | |||||
1261 | $context['linktree'][] = array( |
||||
1262 | 'url' => $scripturl . '?action=admin;area=packages;sa=list;package=' . $_REQUEST['package'], |
||||
1263 | 'name' => $txt['list_file'] |
||||
1264 | ); |
||||
1265 | $context['page_title'] .= ' - ' . $txt['list_file']; |
||||
1266 | $context['sub_template'] = 'list'; |
||||
1267 | |||||
1268 | // The filename... |
||||
1269 | $context['filename'] = $_REQUEST['package']; |
||||
1270 | |||||
1271 | // Let the unpacker do the work. |
||||
1272 | if (is_file($packagesdir . '/' . $context['filename'])) |
||||
1273 | $context['files'] = read_tgz_file($packagesdir . '/' . $context['filename'], null); |
||||
1274 | elseif (is_dir($packagesdir . '/' . $context['filename'])) |
||||
1275 | $context['files'] = listtree($packagesdir . '/' . $context['filename']); |
||||
1276 | } |
||||
1277 | |||||
1278 | /** |
||||
1279 | * Display one of the files in a package. |
||||
1280 | */ |
||||
1281 | function ExamineFile() |
||||
1282 | { |
||||
1283 | global $txt, $scripturl, $context, $sourcedir, $packagesdir, $smcFunc; |
||||
1284 | |||||
1285 | require_once($sourcedir . '/Subs-Package.php'); |
||||
1286 | |||||
1287 | // No package? Show him or her the door. |
||||
1288 | if (!isset($_REQUEST['package']) || $_REQUEST['package'] == '') |
||||
1289 | redirectexit('action=admin;area=packages'); |
||||
1290 | |||||
1291 | // No file? Show him or her the door. |
||||
1292 | if (!isset($_REQUEST['file']) || $_REQUEST['file'] == '') |
||||
1293 | redirectexit('action=admin;area=packages'); |
||||
1294 | |||||
1295 | $_REQUEST['package'] = preg_replace('~[\.]+~', '.', strtr($_REQUEST['package'], array('/' => '_', '\\' => '_'))); |
||||
1296 | $_REQUEST['file'] = preg_replace('~[\.]+~', '.', $_REQUEST['file']); |
||||
1297 | |||||
1298 | if (isset($_REQUEST['raw'])) |
||||
1299 | { |
||||
1300 | if (is_file($packagesdir . '/' . $_REQUEST['package'])) |
||||
1301 | echo read_tgz_file($packagesdir . '/' . $_REQUEST['package'], $_REQUEST['file'], true); |
||||
0 ignored issues
–
show
Are you sure
read_tgz_file($packagesd..._REQUEST['file'], true) of type array|false can be used in echo ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
1302 | elseif (is_dir($packagesdir . '/' . $_REQUEST['package'])) |
||||
1303 | echo file_get_contents($packagesdir . '/' . $_REQUEST['package'] . '/' . $_REQUEST['file']); |
||||
1304 | |||||
1305 | obExit(false); |
||||
1306 | } |
||||
1307 | |||||
1308 | $context['linktree'][count($context['linktree']) - 1] = array( |
||||
1309 | 'url' => $scripturl . '?action=admin;area=packages;sa=list;package=' . $_REQUEST['package'], |
||||
1310 | 'name' => $txt['package_examine_file'] |
||||
1311 | ); |
||||
1312 | $context['page_title'] .= ' - ' . $txt['package_examine_file']; |
||||
1313 | $context['sub_template'] = 'examine'; |
||||
1314 | |||||
1315 | // The filename... |
||||
1316 | $context['package'] = $_REQUEST['package']; |
||||
1317 | $context['filename'] = $_REQUEST['file']; |
||||
1318 | |||||
1319 | // Let the unpacker do the work.... but make sure we handle images properly. |
||||
1320 | if (in_array(strtolower(strrchr($_REQUEST['file'], '.')), array('.bmp', '.gif', '.jpeg', '.jpg', '.png'))) |
||||
1321 | $context['filedata'] = '<img src="' . $scripturl . '?action=admin;area=packages;sa=examine;package=' . $_REQUEST['package'] . ';file=' . $_REQUEST['file'] . ';raw" alt="' . $_REQUEST['file'] . '">'; |
||||
1322 | else |
||||
1323 | { |
||||
1324 | if (is_file($packagesdir . '/' . $_REQUEST['package'])) |
||||
1325 | $context['filedata'] = $smcFunc['htmlspecialchars'](read_tgz_file($packagesdir . '/' . $_REQUEST['package'], $_REQUEST['file'], true)); |
||||
1326 | elseif (is_dir($packagesdir . '/' . $_REQUEST['package'])) |
||||
1327 | $context['filedata'] = $smcFunc['htmlspecialchars'](file_get_contents($packagesdir . '/' . $_REQUEST['package'] . '/' . $_REQUEST['file'])); |
||||
1328 | |||||
1329 | if (strtolower(strrchr($_REQUEST['file'], '.')) == '.php') |
||||
1330 | $context['filedata'] = highlight_php_code($context['filedata']); |
||||
1331 | } |
||||
1332 | } |
||||
1333 | |||||
1334 | /** |
||||
1335 | * Delete a package. |
||||
1336 | */ |
||||
1337 | function PackageRemove() |
||||
1338 | { |
||||
1339 | global $scripturl, $packagesdir; |
||||
1340 | |||||
1341 | // Check it. |
||||
1342 | checkSession('get'); |
||||
1343 | |||||
1344 | // Ack, don't allow deletion of arbitrary files here, could become a security hole somehow! |
||||
1345 | if (!isset($_GET['package']) || $_GET['package'] == 'index.php' || $_GET['package'] == 'backups') |
||||
1346 | redirectexit('action=admin;area=packages;sa=browse'); |
||||
1347 | $_GET['package'] = preg_replace('~[\.]+~', '.', strtr($_GET['package'], array('/' => '_', '\\' => '_'))); |
||||
1348 | |||||
1349 | // Can't delete what's not there. |
||||
1350 | if (file_exists($packagesdir . '/' . $_GET['package']) && (substr($_GET['package'], -4) == '.zip' || substr($_GET['package'], -4) == '.tgz' || substr($_GET['package'], -7) == '.tar.gz' || is_dir($packagesdir . '/' . $_GET['package'])) && $_GET['package'] != 'backups' && substr($_GET['package'], 0, 1) != '.') |
||||
1351 | { |
||||
1352 | create_chmod_control(array($packagesdir . '/' . $_GET['package']), array('destination_url' => $scripturl . '?action=admin;area=packages;sa=remove;package=' . $_GET['package'], 'crash_on_error' => true)); |
||||
1353 | |||||
1354 | if (is_dir($packagesdir . '/' . $_GET['package'])) |
||||
1355 | deltree($packagesdir . '/' . $_GET['package']); |
||||
1356 | else |
||||
1357 | { |
||||
1358 | smf_chmod($packagesdir . '/' . $_GET['package'], 0777); |
||||
1359 | unlink($packagesdir . '/' . $_GET['package']); |
||||
1360 | } |
||||
1361 | } |
||||
1362 | |||||
1363 | redirectexit('action=admin;area=packages;sa=browse'); |
||||
1364 | } |
||||
1365 | |||||
1366 | /** |
||||
1367 | * Browse a list of installed packages. |
||||
1368 | */ |
||||
1369 | function PackageBrowse() |
||||
1370 | { |
||||
1371 | global $txt, $scripturl, $context, $sourcedir, $smcFunc; |
||||
1372 | |||||
1373 | $context['page_title'] .= ' - ' . $txt['browse_packages']; |
||||
1374 | |||||
1375 | $context['forum_version'] = SMF_FULL_VERSION; |
||||
1376 | $context['available_modification'] = array(); |
||||
1377 | $context['available_avatar'] = array(); |
||||
1378 | $context['available_language'] = array(); |
||||
1379 | $context['available_unknown'] = array(); |
||||
1380 | $context['modification_types'] = array('modification', 'avatar', 'language', 'unknown'); |
||||
1381 | |||||
1382 | call_integration_hook('integrate_modification_types'); |
||||
1383 | |||||
1384 | require_once($sourcedir . '/Subs-List.php'); |
||||
1385 | |||||
1386 | foreach ($context['modification_types'] as $type) |
||||
1387 | { |
||||
1388 | // Use the standard templates for showing this. |
||||
1389 | $listOptions = array( |
||||
1390 | 'id' => 'packages_lists_' . $type, |
||||
1391 | 'title' => $txt[$type . '_package'], |
||||
1392 | 'no_items_label' => $txt['no_packages'], |
||||
1393 | 'get_items' => array( |
||||
1394 | 'function' => 'list_getPackages', |
||||
1395 | 'params' => array('type' => $type), |
||||
1396 | ), |
||||
1397 | 'base_href' => $scripturl . '?action=admin;area=packages;sa=browse;type=' . $type, |
||||
1398 | 'default_sort_col' => 'id' . $type, |
||||
1399 | 'columns' => array( |
||||
1400 | 'id' . $type => array( |
||||
1401 | 'header' => array( |
||||
1402 | 'value' => $txt['package_id'], |
||||
1403 | 'style' => 'width: 40px;', |
||||
1404 | ), |
||||
1405 | 'data' => array( |
||||
1406 | 'function' => function($package_md5) use ($type, &$context) |
||||
1407 | { |
||||
1408 | if (isset($context['available_' . $type . ''][$package_md5])) |
||||
1409 | return $context['available_' . $type . ''][$package_md5]['sort_id']; |
||||
1410 | }, |
||||
1411 | ), |
||||
1412 | 'sort' => array( |
||||
1413 | 'default' => 'sort_id', |
||||
1414 | 'reverse' => 'sort_id' |
||||
1415 | ), |
||||
1416 | ), |
||||
1417 | 'mod_name' . $type => array( |
||||
1418 | 'header' => array( |
||||
1419 | 'value' => $txt['mod_name'], |
||||
1420 | 'style' => 'width: 25%;', |
||||
1421 | ), |
||||
1422 | 'data' => array( |
||||
1423 | 'function' => function($package_md5) use ($type, &$context) |
||||
1424 | { |
||||
1425 | if (isset($context['available_' . $type . ''][$package_md5])) |
||||
1426 | return $context['available_' . $type . ''][$package_md5]['name']; |
||||
1427 | }, |
||||
1428 | ), |
||||
1429 | 'sort' => array( |
||||
1430 | 'default' => 'name', |
||||
1431 | 'reverse' => 'name', |
||||
1432 | ), |
||||
1433 | ), |
||||
1434 | 'version' . $type => array( |
||||
1435 | 'header' => array( |
||||
1436 | 'value' => $txt['mod_version'], |
||||
1437 | ), |
||||
1438 | 'data' => array( |
||||
1439 | 'function' => function($package_md5) use ($type, &$context) |
||||
1440 | { |
||||
1441 | if (isset($context['available_' . $type . ''][$package_md5])) |
||||
1442 | return $context['available_' . $type . ''][$package_md5]['version']; |
||||
1443 | }, |
||||
1444 | ), |
||||
1445 | 'sort' => array( |
||||
1446 | 'default' => 'version', |
||||
1447 | 'reverse' => 'version', |
||||
1448 | ), |
||||
1449 | ), |
||||
1450 | 'time_installed' . $type => array( |
||||
1451 | 'header' => array( |
||||
1452 | 'value' => $txt['mod_installed_time'], |
||||
1453 | ), |
||||
1454 | 'data' => array( |
||||
1455 | 'function' => function($package_md5) use ($type, $txt, &$context) |
||||
1456 | { |
||||
1457 | if (isset($context['available_' . $type . ''][$package_md5])) |
||||
1458 | return !empty($context['available_' . $type . ''][$package_md5]['time_installed']) ? timeformat($context['available_' . $type . ''][$package_md5]['time_installed']) : $txt['not_applicable']; |
||||
1459 | }, |
||||
1460 | 'class' => 'smalltext', |
||||
1461 | ), |
||||
1462 | 'sort' => array( |
||||
1463 | 'default' => 'time_installed', |
||||
1464 | 'reverse' => 'time_installed', |
||||
1465 | ), |
||||
1466 | ), |
||||
1467 | 'operations' . $type => array( |
||||
1468 | 'header' => array( |
||||
1469 | 'value' => '', |
||||
1470 | ), |
||||
1471 | 'data' => array( |
||||
1472 | 'function' => function($package_md5) use ($type, &$context, $scripturl, $txt) |
||||
1473 | { |
||||
1474 | if (!isset($context['available_' . $type . ''][$package_md5])) |
||||
1475 | return ''; |
||||
1476 | |||||
1477 | // Rewrite shortcut |
||||
1478 | $package = $context['available_' . $type . ''][$package_md5]; |
||||
1479 | $return = ''; |
||||
1480 | |||||
1481 | if ($package['can_uninstall']) |
||||
1482 | $return = ' |
||||
1483 | <a href="' . $scripturl . '?action=admin;area=packages;sa=uninstall;package=' . $package['filename'] . ';pid=' . $package['installed_id'] . '" class="button">' . $txt['uninstall'] . '</a>'; |
||||
1484 | elseif ($package['can_emulate_uninstall']) |
||||
1485 | $return = ' |
||||
1486 | <a href="' . $scripturl . '?action=admin;area=packages;sa=uninstall;ve=' . $package['can_emulate_uninstall'] . ';package=' . $package['filename'] . ';pid=' . $package['installed_id'] . '" class="button">' . $txt['package_emulate_uninstall'] . ' ' . $package['can_emulate_uninstall'] . '</a>'; |
||||
1487 | elseif ($package['can_upgrade']) |
||||
1488 | $return = ' |
||||
1489 | <a href="' . $scripturl . '?action=admin;area=packages;sa=install;package=' . $package['filename'] . '" class="button">' . $txt['package_upgrade'] . '</a>'; |
||||
1490 | elseif ($package['can_install']) |
||||
1491 | $return = ' |
||||
1492 | <a href="' . $scripturl . '?action=admin;area=packages;sa=install;package=' . $package['filename'] . '" class="button">' . $txt['install_mod'] . '</a>'; |
||||
1493 | elseif ($package['can_emulate_install']) |
||||
1494 | $return = ' |
||||
1495 | <a href="' . $scripturl . '?action=admin;area=packages;sa=install;ve=' . $package['can_emulate_install'] . ';package=' . $package['filename'] . '" class="button">' . $txt['package_emulate_install'] . ' ' . $package['can_emulate_install'] . '</a>'; |
||||
1496 | |||||
1497 | return $return . ' |
||||
1498 | <a href="' . $scripturl . '?action=admin;area=packages;sa=list;package=' . $package['filename'] . '" class="button">' . $txt['list_files'] . '</a> |
||||
1499 | <a href="' . $scripturl . '?action=admin;area=packages;sa=remove;package=' . $package['filename'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '"' . ($package['is_installed'] && $package['is_current'] ? ' data-confirm="' . $txt['package_delete_bad'] . '"' : '') . ' class="button' . ($package['is_installed'] && $package['is_current'] ? ' you_sure' : '') . '">' . $txt['package_delete'] . '</a>'; |
||||
1500 | }, |
||||
1501 | 'class' => 'righttext', |
||||
1502 | ), |
||||
1503 | ), |
||||
1504 | ), |
||||
1505 | ); |
||||
1506 | |||||
1507 | createList($listOptions); |
||||
1508 | } |
||||
1509 | |||||
1510 | $context['sub_template'] = 'browse'; |
||||
1511 | $context['default_list'] = 'packages_lists'; |
||||
1512 | |||||
1513 | $get_versions = $smcFunc['db_query']('', ' |
||||
1514 | SELECT data FROM {db_prefix}admin_info_files WHERE filename={string:versionsfile} AND path={string:smf}', |
||||
1515 | array( |
||||
1516 | 'versionsfile' => 'latest-versions.txt', |
||||
1517 | 'smf' => '/smf/', |
||||
1518 | ) |
||||
1519 | ); |
||||
1520 | |||||
1521 | $data = $smcFunc['db_fetch_assoc']($get_versions); |
||||
1522 | $smcFunc['db_free_result']($get_versions); |
||||
1523 | |||||
1524 | // Decode the data. |
||||
1525 | $items = $smcFunc['json_decode']($data['data'], true); |
||||
1526 | |||||
1527 | $context['emulation_versions'] = preg_replace('~^SMF ~', '', $items); |
||||
1528 | |||||
1529 | // Current SMF version, which is selected by default |
||||
1530 | $context['default_version'] = SMF_VERSION; |
||||
1531 | |||||
1532 | $context['emulation_versions'][] = $context['default_version']; |
||||
1533 | |||||
1534 | // Version we're currently emulating, if any |
||||
1535 | $context['selected_version'] = preg_replace('~^SMF ~', '', $context['forum_version']); |
||||
1536 | } |
||||
1537 | |||||
1538 | /** |
||||
1539 | * Get a listing of all the packages |
||||
1540 | * Determines if the package is a mod, avatar, language package |
||||
1541 | * Determines if the package has been installed or not |
||||
1542 | * |
||||
1543 | * @param int $start The item to start with (not used here) |
||||
1544 | * @param int $items_per_page The number of items to show per page (not used here) |
||||
1545 | * @param string $sort A string indicating how to sort the results |
||||
1546 | * @param string? $params A key for the $packages array |
||||
0 ignored issues
–
show
|
|||||
1547 | * @return array An array of information about the packages |
||||
1548 | */ |
||||
1549 | function list_getPackages($start, $items_per_page, $sort, $params) |
||||
1550 | { |
||||
1551 | global $scripturl, $packagesdir, $context; |
||||
1552 | static $packages, $installed_mods; |
||||
1553 | |||||
1554 | // Start things up |
||||
1555 | if (!isset($packages[$params])) |
||||
1556 | $packages[$params] = array(); |
||||
1557 | |||||
1558 | // We need the packages directory to be writable for this. |
||||
1559 | if (!@is_writable($packagesdir)) |
||||
1560 | create_chmod_control(array($packagesdir), array('destination_url' => $scripturl . '?action=admin;area=packages', 'crash_on_error' => true)); |
||||
1561 | |||||
1562 | $the_version = SMF_VERSION; |
||||
1563 | |||||
1564 | // Here we have a little code to help those who class themselves as something of gods, version emulation ;) |
||||
1565 | if (isset($_GET['version_emulate']) && strtr($_GET['version_emulate'], array('SMF ' => '')) == $the_version) |
||||
1566 | { |
||||
1567 | unset($_SESSION['version_emulate']); |
||||
1568 | } |
||||
1569 | elseif (isset($_GET['version_emulate'])) |
||||
1570 | { |
||||
1571 | if (($_GET['version_emulate'] === 0 || $_GET['version_emulate'] === SMF_FULL_VERSION) && isset($_SESSION['version_emulate'])) |
||||
1572 | unset($_SESSION['version_emulate']); |
||||
1573 | elseif ($_GET['version_emulate'] !== 0) |
||||
1574 | $_SESSION['version_emulate'] = strtr($_GET['version_emulate'], array('-' => ' ', '+' => ' ', 'SMF ' => '')); |
||||
1575 | } |
||||
1576 | if (!empty($_SESSION['version_emulate'])) |
||||
1577 | { |
||||
1578 | $context['forum_version'] = 'SMF ' . $_SESSION['version_emulate']; |
||||
1579 | $the_version = $_SESSION['version_emulate']; |
||||
1580 | } |
||||
1581 | if (isset($_SESSION['single_version_emulate'])) |
||||
1582 | unset($_SESSION['single_version_emulate']); |
||||
1583 | |||||
1584 | if (empty($installed_mods)) |
||||
1585 | { |
||||
1586 | $instmods = loadInstalledPackages(); |
||||
1587 | $installed_mods = array(); |
||||
1588 | // Look through the list of installed mods... |
||||
1589 | foreach ($instmods as $installed_mod) |
||||
1590 | $installed_mods[$installed_mod['package_id']] = array( |
||||
1591 | 'id' => $installed_mod['id'], |
||||
1592 | 'version' => $installed_mod['version'], |
||||
1593 | 'time_installed' => $installed_mod['time_installed'], |
||||
1594 | ); |
||||
1595 | |||||
1596 | // Get a list of all the ids installed, so the latest packages won't include already installed ones. |
||||
1597 | $context['installed_mods'] = array_keys($installed_mods); |
||||
1598 | } |
||||
1599 | |||||
1600 | if ($dir = @opendir($packagesdir)) |
||||
1601 | { |
||||
1602 | $dirs = array(); |
||||
1603 | $sort_id = array( |
||||
1604 | 'modification' => 1, |
||||
1605 | 'avatar' => 1, |
||||
1606 | 'language' => 1, |
||||
1607 | 'unknown' => 1, |
||||
1608 | ); |
||||
1609 | call_integration_hook('integrate_packages_sort_id', array(&$sort_id, &$packages)); |
||||
1610 | |||||
1611 | while ($package = readdir($dir)) |
||||
1612 | { |
||||
1613 | if ($package == '.' || $package == '..' || $package == 'temp' || (!(is_dir($packagesdir . '/' . $package) && file_exists($packagesdir . '/' . $package . '/package-info.xml')) && substr(strtolower($package), -7) != '.tar.gz' && substr(strtolower($package), -4) != '.tgz' && substr(strtolower($package), -4) != '.zip')) |
||||
1614 | continue; |
||||
1615 | |||||
1616 | // Skip directories or files that are named the same. |
||||
1617 | if (is_dir($packagesdir . '/' . $package)) |
||||
1618 | { |
||||
1619 | if (in_array($package, $dirs)) |
||||
1620 | continue; |
||||
1621 | $dirs[] = $package; |
||||
1622 | } |
||||
1623 | elseif (substr(strtolower($package), -7) == '.tar.gz') |
||||
1624 | { |
||||
1625 | if (in_array(substr($package, 0, -7), $dirs)) |
||||
1626 | continue; |
||||
1627 | $dirs[] = substr($package, 0, -7); |
||||
1628 | } |
||||
1629 | elseif (substr(strtolower($package), -4) == '.zip' || substr(strtolower($package), -4) == '.tgz') |
||||
1630 | { |
||||
1631 | if (in_array(substr($package, 0, -4), $dirs)) |
||||
1632 | continue; |
||||
1633 | $dirs[] = substr($package, 0, -4); |
||||
1634 | } |
||||
1635 | |||||
1636 | $packageInfo = getPackageInfo($package); |
||||
1637 | if (!is_array($packageInfo)) |
||||
1638 | continue; |
||||
1639 | |||||
1640 | if (!empty($packageInfo)) |
||||
1641 | { |
||||
1642 | $packageInfo['installed_id'] = isset($installed_mods[$packageInfo['id']]) ? $installed_mods[$packageInfo['id']]['id'] : 0; |
||||
1643 | $packageInfo['time_installed'] = isset($installed_mods[$packageInfo['id']]) ? $installed_mods[$packageInfo['id']]['time_installed'] : 0; |
||||
1644 | |||||
1645 | if (!isset($sort_id[$packageInfo['type']])) |
||||
1646 | $packageInfo['sort_id'] = $sort_id['unknown']; |
||||
1647 | else |
||||
1648 | $packageInfo['sort_id'] = $sort_id[$packageInfo['type']]; |
||||
1649 | |||||
1650 | $packageInfo['is_installed'] = isset($installed_mods[$packageInfo['id']]); |
||||
1651 | $packageInfo['is_current'] = $packageInfo['is_installed'] && ($installed_mods[$packageInfo['id']]['version'] == $packageInfo['version']); |
||||
1652 | $packageInfo['is_newer'] = $packageInfo['is_installed'] && ($installed_mods[$packageInfo['id']]['version'] > $packageInfo['version']); |
||||
1653 | |||||
1654 | $packageInfo['can_install'] = false; |
||||
1655 | $packageInfo['can_uninstall'] = false; |
||||
1656 | $packageInfo['can_upgrade'] = false; |
||||
1657 | $packageInfo['can_emulate_install'] = false; |
||||
1658 | $packageInfo['can_emulate_uninstall'] = false; |
||||
1659 | |||||
1660 | // This package is currently NOT installed. Check if it can be. |
||||
1661 | if (!$packageInfo['is_installed'] && $packageInfo['xml']->exists('install')) |
||||
1662 | { |
||||
1663 | // Check if there's an install for *THIS* version of SMF. |
||||
1664 | $installs = $packageInfo['xml']->set('install'); |
||||
1665 | foreach ($installs as $install) |
||||
1666 | { |
||||
1667 | if (!$install->exists('@for') || matchPackageVersion($the_version, $install->fetch('@for'))) |
||||
1668 | { |
||||
1669 | // Okay, this one is good to go. |
||||
1670 | $packageInfo['can_install'] = true; |
||||
1671 | break; |
||||
1672 | } |
||||
1673 | } |
||||
1674 | |||||
1675 | // no install found for this version, lets see if one exists for another |
||||
1676 | if ($packageInfo['can_install'] === false && $install->exists('@for') && empty($_SESSION['version_emulate'])) |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1677 | { |
||||
1678 | $reset = true; |
||||
1679 | |||||
1680 | // Get the highest install version that is available from the package |
||||
1681 | foreach ($installs as $install) |
||||
1682 | { |
||||
1683 | $packageInfo['can_emulate_install'] = matchHighestPackageVersion($install->fetch('@for'), $reset, $the_version); |
||||
1684 | $reset = false; |
||||
1685 | } |
||||
1686 | } |
||||
1687 | } |
||||
1688 | // An already installed, but old, package. Can we upgrade it? |
||||
1689 | elseif ($packageInfo['is_installed'] && !$packageInfo['is_current'] && $packageInfo['xml']->exists('upgrade')) |
||||
1690 | { |
||||
1691 | $upgrades = $packageInfo['xml']->set('upgrade'); |
||||
1692 | |||||
1693 | // First go through, and check against the current version of SMF. |
||||
1694 | foreach ($upgrades as $upgrade) |
||||
1695 | { |
||||
1696 | // Even if it is for this SMF, is it for the installed version of the mod? |
||||
1697 | if (!$upgrade->exists('@for') || matchPackageVersion($the_version, $upgrade->fetch('@for'))) |
||||
1698 | if (!$upgrade->exists('@from') || matchPackageVersion($installed_mods[$packageInfo['id']]['version'], $upgrade->fetch('@from'))) |
||||
1699 | { |
||||
1700 | $packageInfo['can_upgrade'] = true; |
||||
1701 | break; |
||||
1702 | } |
||||
1703 | } |
||||
1704 | } |
||||
1705 | // Note that it has to be the current version to be uninstallable. Shucks. |
||||
1706 | elseif ($packageInfo['is_installed'] && $packageInfo['is_current'] && $packageInfo['xml']->exists('uninstall')) |
||||
1707 | { |
||||
1708 | $uninstalls = $packageInfo['xml']->set('uninstall'); |
||||
1709 | |||||
1710 | // Can we find any uninstallation methods that work for this SMF version? |
||||
1711 | foreach ($uninstalls as $uninstall) |
||||
1712 | { |
||||
1713 | if (!$uninstall->exists('@for') || matchPackageVersion($the_version, $uninstall->fetch('@for'))) |
||||
1714 | { |
||||
1715 | $packageInfo['can_uninstall'] = true; |
||||
1716 | break; |
||||
1717 | } |
||||
1718 | } |
||||
1719 | |||||
1720 | // no uninstall found for this version, lets see if one exists for another |
||||
1721 | if ($packageInfo['can_uninstall'] === false && $uninstall->exists('@for') && empty($_SESSION['version_emulate'])) |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
1722 | { |
||||
1723 | $reset = true; |
||||
1724 | |||||
1725 | // Get the highest install version that is available from the package |
||||
1726 | foreach ($uninstalls as $uninstall) |
||||
1727 | { |
||||
1728 | $packageInfo['can_emulate_uninstall'] = matchHighestPackageVersion($uninstall->fetch('@for'), $reset, $the_version); |
||||
1729 | $reset = false; |
||||
1730 | } |
||||
1731 | } |
||||
1732 | } |
||||
1733 | |||||
1734 | // Save some memory by not passing the xmlArray object into context. |
||||
1735 | unset($packageInfo['xml']); |
||||
1736 | |||||
1737 | if (isset($sort_id[$packageInfo['type']], $packages[$packageInfo['type']], $context['available_' . $packageInfo['type']]) && $params == $packageInfo['type']) |
||||
1738 | { |
||||
1739 | $sort_id[$packageInfo['type']]++; |
||||
1740 | $packages[$packageInfo['type']][strtolower($packageInfo[$sort]) . '_' . $sort_id[$packageInfo['type']]] = md5($package); |
||||
1741 | $context['available_' . $packageInfo['type']][md5($package)] = $packageInfo; |
||||
1742 | } |
||||
1743 | elseif (!isset($sort_id[$packageInfo['type']], $packages[$packageInfo['type']], $context['available_' . $packageInfo['type']]) && $params == 'unknown') |
||||
1744 | { |
||||
1745 | $packageInfo['sort_id'] = $sort_id['unknown']; |
||||
1746 | $sort_id['unknown']++; |
||||
1747 | $packages['unknown'][strtolower($packageInfo[$sort]) . '_' . $sort_id['unknown']] = md5($package); |
||||
1748 | $context['available_unknown'][md5($package)] = $packageInfo; |
||||
1749 | } |
||||
1750 | } |
||||
1751 | } |
||||
1752 | closedir($dir); |
||||
1753 | } |
||||
1754 | |||||
1755 | if (isset($_GET['type']) && $_GET['type'] == $params) |
||||
1756 | { |
||||
1757 | if (isset($_GET['desc'])) |
||||
1758 | krsort($packages[$params]); |
||||
1759 | else |
||||
1760 | ksort($packages[$params]); |
||||
1761 | } |
||||
1762 | |||||
1763 | return $packages[$params]; |
||||
1764 | } |
||||
1765 | |||||
1766 | /** |
||||
1767 | * Used when a temp FTP access is needed to package functions |
||||
1768 | */ |
||||
1769 | function PackageOptions() |
||||
1770 | { |
||||
1771 | global $txt, $context, $modSettings, $smcFunc; |
||||
1772 | |||||
1773 | if (isset($_POST['save'])) |
||||
1774 | { |
||||
1775 | checkSession(); |
||||
1776 | |||||
1777 | updateSettings(array( |
||||
1778 | 'package_server' => trim($smcFunc['htmlspecialchars']($_POST['pack_server'])), |
||||
1779 | 'package_port' => trim($smcFunc['htmlspecialchars']($_POST['pack_port'])), |
||||
1780 | 'package_username' => trim($smcFunc['htmlspecialchars']($_POST['pack_user'])), |
||||
1781 | 'package_make_backups' => !empty($_POST['package_make_backups']), |
||||
1782 | 'package_make_full_backups' => !empty($_POST['package_make_full_backups']) |
||||
1783 | )); |
||||
1784 | $_SESSION['adm-save'] = true; |
||||
1785 | |||||
1786 | redirectexit('action=admin;area=packages;sa=options'); |
||||
1787 | } |
||||
1788 | |||||
1789 | if (preg_match('~^/home\d*/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match)) |
||||
1790 | $default_username = $match[1]; |
||||
1791 | else |
||||
1792 | $default_username = ''; |
||||
1793 | |||||
1794 | $context['page_title'] = $txt['package_settings']; |
||||
1795 | $context['sub_template'] = 'install_options'; |
||||
1796 | |||||
1797 | $context['package_ftp_server'] = isset($modSettings['package_server']) ? $modSettings['package_server'] : 'localhost'; |
||||
1798 | $context['package_ftp_port'] = isset($modSettings['package_port']) ? $modSettings['package_port'] : '21'; |
||||
1799 | $context['package_ftp_username'] = isset($modSettings['package_username']) ? $modSettings['package_username'] : $default_username; |
||||
1800 | $context['package_make_backups'] = !empty($modSettings['package_make_backups']); |
||||
1801 | $context['package_make_full_backups'] = !empty($modSettings['package_make_full_backups']); |
||||
1802 | |||||
1803 | if (!empty($_SESSION['adm-save'])) |
||||
1804 | { |
||||
1805 | $context['saved_successful'] = true; |
||||
1806 | unset ($_SESSION['adm-save']); |
||||
1807 | } |
||||
1808 | } |
||||
1809 | |||||
1810 | /** |
||||
1811 | * List operations |
||||
1812 | */ |
||||
1813 | function ViewOperations() |
||||
1814 | { |
||||
1815 | global $context, $txt, $sourcedir, $packagesdir, $smcFunc, $modSettings; |
||||
1816 | |||||
1817 | // Can't be in here buddy. |
||||
1818 | isAllowedTo('admin_forum'); |
||||
1819 | |||||
1820 | // We need to know the operation key for the search and replace, mod file looking at, is it a board mod? |
||||
1821 | if (!isset($_REQUEST['operation_key'], $_REQUEST['filename']) && !is_numeric($_REQUEST['operation_key'])) |
||||
1822 | fatal_lang_error('operation_invalid', 'general'); |
||||
1823 | |||||
1824 | // Load the required file. |
||||
1825 | require_once($sourcedir . '/Subs-Package.php'); |
||||
1826 | |||||
1827 | // Uninstalling the mod? |
||||
1828 | $reverse = isset($_REQUEST['reverse']) ? true : false; |
||||
1829 | |||||
1830 | // Get the base name. |
||||
1831 | $context['filename'] = preg_replace('~[\.]+~', '.', $_REQUEST['package']); |
||||
1832 | |||||
1833 | // We need to extract this again. |
||||
1834 | if (is_file($packagesdir . '/' . $context['filename'])) |
||||
1835 | { |
||||
1836 | $context['extracted_files'] = read_tgz_file($packagesdir . '/' . $context['filename'], $packagesdir . '/temp'); |
||||
1837 | |||||
1838 | if ($context['extracted_files'] && !file_exists($packagesdir . '/temp/package-info.xml')) |
||||
1839 | foreach ($context['extracted_files'] as $file) |
||||
1840 | if (basename($file['filename']) == 'package-info.xml') |
||||
1841 | { |
||||
1842 | $context['base_path'] = dirname($file['filename']) . '/'; |
||||
1843 | break; |
||||
1844 | } |
||||
1845 | |||||
1846 | if (!isset($context['base_path'])) |
||||
1847 | $context['base_path'] = ''; |
||||
1848 | } |
||||
1849 | elseif (is_dir($packagesdir . '/' . $context['filename'])) |
||||
1850 | { |
||||
1851 | copytree($packagesdir . '/' . $context['filename'], $packagesdir . '/temp'); |
||||
1852 | $context['extracted_files'] = listtree($packagesdir . '/temp'); |
||||
1853 | $context['base_path'] = ''; |
||||
1854 | } |
||||
1855 | |||||
1856 | // Load up any custom themes we may want to install into... |
||||
1857 | $request = $smcFunc['db_query']('', ' |
||||
1858 | SELECT id_theme, variable, value |
||||
1859 | FROM {db_prefix}themes |
||||
1860 | WHERE (id_theme = {int:default_theme} OR id_theme IN ({array_int:known_theme_list})) |
||||
1861 | AND variable IN ({string:name}, {string:theme_dir})', |
||||
1862 | array( |
||||
1863 | 'known_theme_list' => explode(',', $modSettings['knownThemes']), |
||||
1864 | 'default_theme' => 1, |
||||
1865 | 'name' => 'name', |
||||
1866 | 'theme_dir' => 'theme_dir', |
||||
1867 | ) |
||||
1868 | ); |
||||
1869 | $theme_paths = array(); |
||||
1870 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||||
1871 | $theme_paths[$row['id_theme']][$row['variable']] = $row['value']; |
||||
1872 | $smcFunc['db_free_result']($request); |
||||
1873 | |||||
1874 | // If we're viewing uninstall operations, only consider themes that |
||||
1875 | // the package is actually installed into. |
||||
1876 | if (isset($_REQUEST['reverse']) && !empty($_REQUEST['install_id'])) |
||||
1877 | { |
||||
1878 | $install_id = (int) $_REQUEST['install_id']; |
||||
1879 | if ($install_id > 0) |
||||
1880 | { |
||||
1881 | $old_themes = array(); |
||||
0 ignored issues
–
show
|
|||||
1882 | $request = $smcFunc['db_query']('', ' |
||||
1883 | SELECT themes_installed |
||||
1884 | FROM {db_prefix}log_packages |
||||
1885 | WHERE id_install = {int:install_id}', |
||||
1886 | array( |
||||
1887 | 'install_id' => $install_id, |
||||
1888 | ) |
||||
1889 | ); |
||||
1890 | |||||
1891 | if ($smcFunc['db_num_rows']($request) == 1) |
||||
1892 | { |
||||
1893 | list ($old_themes) = $smcFunc['db_fetch_row']($request); |
||||
1894 | $old_themes = explode(',', $old_themes); |
||||
1895 | |||||
1896 | foreach ($theme_paths as $id => $data) |
||||
1897 | if ($id != 1 && !in_array($id, $old_themes)) |
||||
1898 | unset($theme_paths[$id]); |
||||
1899 | } |
||||
1900 | $smcFunc['db_free_result']($request); |
||||
1901 | } |
||||
1902 | } |
||||
1903 | |||||
1904 | // Boardmod? |
||||
1905 | if (isset($_REQUEST['boardmod'])) |
||||
1906 | $mod_actions = parseBoardMod(@file_get_contents($packagesdir . '/temp/' . $context['base_path'] . $_REQUEST['filename']), true, $reverse, $theme_paths); |
||||
1907 | else |
||||
1908 | $mod_actions = parseModification(@file_get_contents($packagesdir . '/temp/' . $context['base_path'] . $_REQUEST['filename']), true, $reverse, $theme_paths); |
||||
1909 | |||||
1910 | // Ok lets get the content of the file. |
||||
1911 | $context['operations'] = array( |
||||
1912 | 'search' => strtr($smcFunc['htmlspecialchars']($mod_actions[$_REQUEST['operation_key']]['search_original']), array('[' => '[', ']' => ']')), |
||||
1913 | 'replace' => strtr($smcFunc['htmlspecialchars']($mod_actions[$_REQUEST['operation_key']]['replace_original']), array('[' => '[', ']' => ']')), |
||||
1914 | 'position' => $mod_actions[$_REQUEST['operation_key']]['position'], |
||||
1915 | ); |
||||
1916 | |||||
1917 | // Let's do some formatting... |
||||
1918 | $operation_text = $context['operations']['position'] == 'replace' ? 'operation_replace' : ($context['operations']['position'] == 'before' ? 'operation_after' : 'operation_before'); |
||||
1919 | $context['operations']['search'] = parse_bbc('[code=' . $txt['operation_find'] . ']' . ($context['operations']['position'] == 'end' ? '?>' : $context['operations']['search']) . '[/code]'); |
||||
1920 | $context['operations']['replace'] = parse_bbc('[code=' . $txt[$operation_text] . ']' . $context['operations']['replace'] . '[/code]'); |
||||
1921 | |||||
1922 | // No layers |
||||
1923 | $context['template_layers'] = array(); |
||||
1924 | $context['sub_template'] = 'view_operations'; |
||||
1925 | } |
||||
1926 | |||||
1927 | /** |
||||
1928 | * Allow the admin to reset permissions on files. |
||||
1929 | */ |
||||
1930 | function PackagePermissions() |
||||
1931 | { |
||||
1932 | global $context, $txt, $modSettings, $boarddir, $sourcedir, $cachedir, $smcFunc, $package_ftp; |
||||
1933 | |||||
1934 | // Let's try and be good, yes? |
||||
1935 | checkSession('get'); |
||||
1936 | |||||
1937 | // If we're restoring permissions this is just a pass through really. |
||||
1938 | if (isset($_GET['restore'])) |
||||
1939 | { |
||||
1940 | create_chmod_control(array(), array(), true); |
||||
1941 | fatal_lang_error('no_access', false); |
||||
1942 | } |
||||
1943 | |||||
1944 | // This is a memory eat. |
||||
1945 | setMemoryLimit('128M'); |
||||
1946 | @set_time_limit(600); |
||||
1947 | |||||
1948 | // Load up some FTP stuff. |
||||
1949 | create_chmod_control(); |
||||
1950 | |||||
1951 | if (empty($package_ftp) && !isset($_POST['skip_ftp'])) |
||||
1952 | { |
||||
1953 | require_once($sourcedir . '/Class-Package.php'); |
||||
1954 | $ftp = new ftp_connection(null); |
||||
1955 | list ($username, $detect_path, $found_path) = $ftp->detect_path($boarddir); |
||||
1956 | |||||
1957 | $context['package_ftp'] = array( |
||||
1958 | 'server' => isset($modSettings['package_server']) ? $modSettings['package_server'] : 'localhost', |
||||
1959 | 'port' => isset($modSettings['package_port']) ? $modSettings['package_port'] : '21', |
||||
1960 | 'username' => empty($username) ? (isset($modSettings['package_username']) ? $modSettings['package_username'] : '') : $username, |
||||
1961 | 'path' => $detect_path, |
||||
1962 | 'form_elements_only' => true, |
||||
1963 | ); |
||||
1964 | } |
||||
1965 | else |
||||
1966 | $context['ftp_connected'] = true; |
||||
1967 | |||||
1968 | // Define the template. |
||||
1969 | $context['page_title'] = $txt['package_file_perms']; |
||||
1970 | $context['sub_template'] = 'file_permissions'; |
||||
1971 | |||||
1972 | // Define what files we're interested in, as a tree. |
||||
1973 | $context['file_tree'] = array( |
||||
1974 | strtr($boarddir, array('\\' => '/')) => array( |
||||
1975 | 'type' => 'dir', |
||||
1976 | 'contents' => array( |
||||
1977 | 'agreement.txt' => array( |
||||
1978 | 'type' => 'file', |
||||
1979 | 'writable_on' => 'standard', |
||||
1980 | ), |
||||
1981 | 'Settings.php' => array( |
||||
1982 | 'type' => 'file', |
||||
1983 | 'writable_on' => 'restrictive', |
||||
1984 | ), |
||||
1985 | 'Settings_bak.php' => array( |
||||
1986 | 'type' => 'file', |
||||
1987 | 'writable_on' => 'restrictive', |
||||
1988 | ), |
||||
1989 | 'attachments' => array( |
||||
1990 | 'type' => 'dir', |
||||
1991 | 'writable_on' => 'restrictive', |
||||
1992 | ), |
||||
1993 | 'avatars' => array( |
||||
1994 | 'type' => 'dir', |
||||
1995 | 'writable_on' => 'standard', |
||||
1996 | ), |
||||
1997 | 'cache' => array( |
||||
1998 | 'type' => 'dir', |
||||
1999 | 'writable_on' => 'restrictive', |
||||
2000 | ), |
||||
2001 | 'custom_avatar_dir' => array( |
||||
2002 | 'type' => 'dir', |
||||
2003 | 'writable_on' => 'restrictive', |
||||
2004 | ), |
||||
2005 | 'Smileys' => array( |
||||
2006 | 'type' => 'dir_recursive', |
||||
2007 | 'writable_on' => 'standard', |
||||
2008 | ), |
||||
2009 | 'Sources' => array( |
||||
2010 | 'type' => 'dir_recursive', |
||||
2011 | 'list_contents' => true, |
||||
2012 | 'writable_on' => 'standard', |
||||
2013 | 'contents' => array( |
||||
2014 | 'tasks' => array( |
||||
2015 | 'type' => 'dir', |
||||
2016 | 'list_contents' => true, |
||||
2017 | ), |
||||
2018 | ), |
||||
2019 | ), |
||||
2020 | 'Themes' => array( |
||||
2021 | 'type' => 'dir_recursive', |
||||
2022 | 'writable_on' => 'standard', |
||||
2023 | 'contents' => array( |
||||
2024 | 'default' => array( |
||||
2025 | 'type' => 'dir_recursive', |
||||
2026 | 'list_contents' => true, |
||||
2027 | 'contents' => array( |
||||
2028 | 'languages' => array( |
||||
2029 | 'type' => 'dir', |
||||
2030 | 'list_contents' => true, |
||||
2031 | ), |
||||
2032 | ), |
||||
2033 | ), |
||||
2034 | ), |
||||
2035 | ), |
||||
2036 | 'Packages' => array( |
||||
2037 | 'type' => 'dir', |
||||
2038 | 'writable_on' => 'standard', |
||||
2039 | 'contents' => array( |
||||
2040 | 'temp' => array( |
||||
2041 | 'type' => 'dir', |
||||
2042 | ), |
||||
2043 | 'backup' => array( |
||||
2044 | 'type' => 'dir', |
||||
2045 | ), |
||||
2046 | ), |
||||
2047 | ), |
||||
2048 | ), |
||||
2049 | ), |
||||
2050 | ); |
||||
2051 | |||||
2052 | // Directories that can move. |
||||
2053 | if (substr($sourcedir, 0, strlen($boarddir)) != $boarddir) |
||||
2054 | { |
||||
2055 | unset($context['file_tree'][strtr($boarddir, array('\\' => '/'))]['contents']['Sources']); |
||||
2056 | $context['file_tree'][strtr($sourcedir, array('\\' => '/'))] = array( |
||||
2057 | 'type' => 'dir', |
||||
2058 | 'list_contents' => true, |
||||
2059 | 'writable_on' => 'standard', |
||||
2060 | ); |
||||
2061 | } |
||||
2062 | |||||
2063 | // Moved the cache? |
||||
2064 | if (substr($cachedir, 0, strlen($boarddir)) != $boarddir) |
||||
2065 | { |
||||
2066 | unset($context['file_tree'][strtr($boarddir, array('\\' => '/'))]['contents']['cache']); |
||||
2067 | $context['file_tree'][strtr($cachedir, array('\\' => '/'))] = array( |
||||
2068 | 'type' => 'dir', |
||||
2069 | 'list_contents' => false, |
||||
2070 | 'writable_on' => 'restrictive', |
||||
2071 | ); |
||||
2072 | } |
||||
2073 | |||||
2074 | // Are we using multiple attachment directories? |
||||
2075 | if (!empty($modSettings['currentAttachmentUploadDir'])) |
||||
2076 | { |
||||
2077 | unset($context['file_tree'][strtr($boarddir, array('\\' => '/'))]['contents']['attachments']); |
||||
2078 | |||||
2079 | if (!is_array($modSettings['attachmentUploadDir'])) |
||||
2080 | $modSettings['attachmentUploadDir'] = $smcFunc['json_decode']($modSettings['attachmentUploadDir'], true); |
||||
2081 | |||||
2082 | // @todo Should we suggest non-current directories be read only? |
||||
2083 | foreach ($modSettings['attachmentUploadDir'] as $dir) |
||||
2084 | $context['file_tree'][strtr($dir, array('\\' => '/'))] = array( |
||||
2085 | 'type' => 'dir', |
||||
2086 | 'writable_on' => 'restrictive', |
||||
2087 | ); |
||||
2088 | } |
||||
2089 | elseif (substr($modSettings['attachmentUploadDir'], 0, strlen($boarddir)) != $boarddir) |
||||
2090 | { |
||||
2091 | unset($context['file_tree'][strtr($boarddir, array('\\' => '/'))]['contents']['attachments']); |
||||
2092 | $context['file_tree'][strtr($modSettings['attachmentUploadDir'], array('\\' => '/'))] = array( |
||||
2093 | 'type' => 'dir', |
||||
2094 | 'writable_on' => 'restrictive', |
||||
2095 | ); |
||||
2096 | } |
||||
2097 | |||||
2098 | if (substr($modSettings['smileys_dir'], 0, strlen($boarddir)) != $boarddir) |
||||
2099 | { |
||||
2100 | unset($context['file_tree'][strtr($boarddir, array('\\' => '/'))]['contents']['Smileys']); |
||||
2101 | $context['file_tree'][strtr($modSettings['smileys_dir'], array('\\' => '/'))] = array( |
||||
2102 | 'type' => 'dir_recursive', |
||||
2103 | 'writable_on' => 'standard', |
||||
2104 | ); |
||||
2105 | } |
||||
2106 | if (substr($modSettings['avatar_directory'], 0, strlen($boarddir)) != $boarddir) |
||||
2107 | { |
||||
2108 | unset($context['file_tree'][strtr($boarddir, array('\\' => '/'))]['contents']['avatars']); |
||||
2109 | $context['file_tree'][strtr($modSettings['avatar_directory'], array('\\' => '/'))] = array( |
||||
2110 | 'type' => 'dir', |
||||
2111 | 'writable_on' => 'standard', |
||||
2112 | ); |
||||
2113 | } |
||||
2114 | if (isset($modSettings['custom_avatar_dir']) && substr($modSettings['custom_avatar_dir'], 0, strlen($boarddir)) != $boarddir) |
||||
2115 | { |
||||
2116 | unset($context['file_tree'][strtr($boarddir, array('\\' => '/'))]['contents']['custom_avatar_dir']); |
||||
2117 | $context['file_tree'][strtr($modSettings['custom_avatar_dir'], array('\\' => '/'))] = array( |
||||
2118 | 'type' => 'dir', |
||||
2119 | 'writable_on' => 'restrictive', |
||||
2120 | ); |
||||
2121 | } |
||||
2122 | |||||
2123 | // Load up any custom themes. |
||||
2124 | $request = $smcFunc['db_query']('', ' |
||||
2125 | SELECT value |
||||
2126 | FROM {db_prefix}themes |
||||
2127 | WHERE id_theme > {int:default_theme_id} |
||||
2128 | AND id_member = {int:guest_id} |
||||
2129 | AND variable = {string:theme_dir} |
||||
2130 | ORDER BY value ASC', |
||||
2131 | array( |
||||
2132 | 'default_theme_id' => 1, |
||||
2133 | 'guest_id' => 0, |
||||
2134 | 'theme_dir' => 'theme_dir', |
||||
2135 | ) |
||||
2136 | ); |
||||
2137 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||||
2138 | { |
||||
2139 | if (substr(strtolower(strtr($row['value'], array('\\' => '/'))), 0, strlen($boarddir) + 7) == strtolower(strtr($boarddir, array('\\' => '/')) . '/Themes')) |
||||
2140 | $context['file_tree'][strtr($boarddir, array('\\' => '/'))]['contents']['Themes']['contents'][substr($row['value'], strlen($boarddir) + 8)] = array( |
||||
2141 | 'type' => 'dir_recursive', |
||||
2142 | 'list_contents' => true, |
||||
2143 | 'contents' => array( |
||||
2144 | 'languages' => array( |
||||
2145 | 'type' => 'dir', |
||||
2146 | 'list_contents' => true, |
||||
2147 | ), |
||||
2148 | ), |
||||
2149 | ); |
||||
2150 | else |
||||
2151 | { |
||||
2152 | $context['file_tree'][strtr($row['value'], array('\\' => '/'))] = array( |
||||
2153 | 'type' => 'dir_recursive', |
||||
2154 | 'list_contents' => true, |
||||
2155 | 'contents' => array( |
||||
2156 | 'languages' => array( |
||||
2157 | 'type' => 'dir', |
||||
2158 | 'list_contents' => true, |
||||
2159 | ), |
||||
2160 | ), |
||||
2161 | ); |
||||
2162 | } |
||||
2163 | } |
||||
2164 | $smcFunc['db_free_result']($request); |
||||
2165 | |||||
2166 | // If we're submitting then let's move on to another function to keep things cleaner.. |
||||
2167 | if (isset($_POST['action_changes'])) |
||||
2168 | return PackagePermissionsAction(); |
||||
2169 | |||||
2170 | $context['look_for'] = array(); |
||||
2171 | // Are we looking for a particular tree - normally an expansion? |
||||
2172 | if (!empty($_REQUEST['find'])) |
||||
2173 | $context['look_for'][] = base64_decode($_REQUEST['find']); |
||||
2174 | // Only that tree? |
||||
2175 | $context['only_find'] = isset($_GET['xml']) && !empty($_REQUEST['onlyfind']) ? $_REQUEST['onlyfind'] : ''; |
||||
2176 | if ($context['only_find']) |
||||
2177 | $context['look_for'][] = $context['only_find']; |
||||
2178 | |||||
2179 | // Have we got a load of back-catalogue trees to expand from a submit etc? |
||||
2180 | if (!empty($_GET['back_look'])) |
||||
2181 | { |
||||
2182 | $potententialTrees = $smcFunc['json_decode'](base64_decode($_GET['back_look']), true); |
||||
2183 | foreach ($potententialTrees as $tree) |
||||
2184 | $context['look_for'][] = $tree; |
||||
2185 | } |
||||
2186 | // ... maybe posted? |
||||
2187 | if (!empty($_POST['back_look'])) |
||||
2188 | $context['only_find'] = array_merge($context['only_find'], $_POST['back_look']); |
||||
2189 | |||||
2190 | $context['back_look_data'] = base64_encode($smcFunc['json_encode'](array_slice($context['look_for'], 0, 15))); |
||||
2191 | |||||
2192 | // Are we finding more files than first thought? |
||||
2193 | $context['file_offset'] = !empty($_REQUEST['fileoffset']) ? (int) $_REQUEST['fileoffset'] : 0; |
||||
2194 | // Don't list more than this many files in a directory. |
||||
2195 | $context['file_limit'] = 150; |
||||
2196 | |||||
2197 | // How many levels shall we show? |
||||
2198 | $context['default_level'] = empty($context['only_find']) ? 2 : 25; |
||||
2199 | |||||
2200 | // This will be used if we end up catching XML data. |
||||
2201 | $context['xml_data'] = array( |
||||
2202 | 'roots' => array( |
||||
2203 | 'identifier' => 'root', |
||||
2204 | 'children' => array( |
||||
2205 | array( |
||||
2206 | 'value' => preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $context['only_find']), |
||||
2207 | ), |
||||
2208 | ), |
||||
2209 | ), |
||||
2210 | 'folders' => array( |
||||
2211 | 'identifier' => 'folder', |
||||
2212 | 'children' => array(), |
||||
2213 | ), |
||||
2214 | ); |
||||
2215 | |||||
2216 | foreach ($context['file_tree'] as $path => $data) |
||||
2217 | { |
||||
2218 | // Run this directory. |
||||
2219 | if (file_exists($path) && (empty($context['only_find']) || substr($context['only_find'], 0, strlen($path)) == $path)) |
||||
2220 | { |
||||
2221 | // Get the first level down only. |
||||
2222 | fetchPerms__recursive($path, $context['file_tree'][$path], 1); |
||||
2223 | $context['file_tree'][$path]['perms'] = array( |
||||
2224 | 'chmod' => @is_writable($path), |
||||
2225 | 'perms' => @fileperms($path), |
||||
2226 | ); |
||||
2227 | } |
||||
2228 | else |
||||
2229 | unset($context['file_tree'][$path]); |
||||
2230 | } |
||||
2231 | |||||
2232 | // Is this actually xml? |
||||
2233 | if (isset($_GET['xml'])) |
||||
2234 | { |
||||
2235 | loadTemplate('Xml'); |
||||
2236 | $context['sub_template'] = 'generic_xml'; |
||||
2237 | $context['template_layers'] = array(); |
||||
2238 | } |
||||
2239 | } |
||||
2240 | |||||
2241 | /** |
||||
2242 | * Checkes the permissions of all the areas that will be affected by the package |
||||
2243 | * |
||||
2244 | * @param string $path The path to the directiory to check permissions for |
||||
2245 | * @param array $data An array of data about the directory |
||||
2246 | * @param int $level How far deep to go |
||||
2247 | */ |
||||
2248 | function fetchPerms__recursive($path, &$data, $level) |
||||
2249 | { |
||||
2250 | global $context; |
||||
2251 | |||||
2252 | $isLikelyPath = false; |
||||
2253 | foreach ($context['look_for'] as $possiblePath) |
||||
2254 | if (substr($possiblePath, 0, strlen($path)) == $path) |
||||
2255 | $isLikelyPath = true; |
||||
2256 | |||||
2257 | // Is this where we stop? |
||||
2258 | if (isset($_GET['xml']) && !empty($context['look_for']) && !$isLikelyPath) |
||||
2259 | return; |
||||
2260 | elseif ($level > $context['default_level'] && !$isLikelyPath) |
||||
2261 | return; |
||||
2262 | |||||
2263 | // Are we actually interested in saving this data? |
||||
2264 | $save_data = empty($context['only_find']) || $context['only_find'] == $path; |
||||
2265 | |||||
2266 | // @todo Shouldn't happen - but better error message? |
||||
2267 | if (!is_dir($path)) |
||||
2268 | fatal_lang_error('no_access', false); |
||||
2269 | |||||
2270 | // This is where we put stuff we've found for sorting. |
||||
2271 | $foundData = array( |
||||
2272 | 'files' => array(), |
||||
2273 | 'folders' => array(), |
||||
2274 | ); |
||||
2275 | |||||
2276 | $dh = opendir($path); |
||||
2277 | while ($entry = readdir($dh)) |
||||
0 ignored issues
–
show
It seems like
$dh can also be of type false ; however, parameter $dir_handle of readdir() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
2278 | { |
||||
2279 | // Some kind of file? |
||||
2280 | if (is_file($path . '/' . $entry)) |
||||
2281 | { |
||||
2282 | // Are we listing PHP files in this directory? |
||||
2283 | if ($save_data && !empty($data['list_contents']) && substr($entry, -4) == '.php') |
||||
2284 | $foundData['files'][$entry] = true; |
||||
2285 | // A file we were looking for. |
||||
2286 | elseif ($save_data && isset($data['contents'][$entry])) |
||||
2287 | $foundData['files'][$entry] = true; |
||||
2288 | } |
||||
2289 | // It's a directory - we're interested one way or another, probably... |
||||
2290 | elseif ($entry != '.' && $entry != '..') |
||||
2291 | { |
||||
2292 | // Going further? |
||||
2293 | if ((!empty($data['type']) && $data['type'] == 'dir_recursive') || (isset($data['contents'][$entry]) && (!empty($data['contents'][$entry]['list_contents']) || (!empty($data['contents'][$entry]['type']) && $data['contents'][$entry]['type'] == 'dir_recursive')))) |
||||
2294 | { |
||||
2295 | if (!isset($data['contents'][$entry])) |
||||
2296 | $foundData['folders'][$entry] = 'dir_recursive'; |
||||
2297 | else |
||||
2298 | $foundData['folders'][$entry] = true; |
||||
2299 | |||||
2300 | // If this wasn't expected inherit the recusiveness... |
||||
2301 | if (!isset($data['contents'][$entry])) |
||||
2302 | // We need to do this as we will be going all recursive. |
||||
2303 | $data['contents'][$entry] = array( |
||||
2304 | 'type' => 'dir_recursive', |
||||
2305 | ); |
||||
2306 | |||||
2307 | // Actually do the recursive stuff... |
||||
2308 | fetchPerms__recursive($path . '/' . $entry, $data['contents'][$entry], $level + 1); |
||||
2309 | } |
||||
2310 | // Maybe it is a folder we are not descending into. |
||||
2311 | elseif (isset($data['contents'][$entry])) |
||||
2312 | $foundData['folders'][$entry] = true; |
||||
2313 | // Otherwise we stop here. |
||||
2314 | } |
||||
2315 | } |
||||
2316 | closedir($dh); |
||||
0 ignored issues
–
show
It seems like
$dh can also be of type false ; however, parameter $dir_handle of closedir() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
2317 | |||||
2318 | // Nothing to see here? |
||||
2319 | if (!$save_data) |
||||
2320 | return; |
||||
2321 | |||||
2322 | // Now actually add the data, starting with the folders. |
||||
2323 | ksort($foundData['folders']); |
||||
2324 | foreach ($foundData['folders'] as $folder => $type) |
||||
2325 | { |
||||
2326 | $additional_data = array( |
||||
2327 | 'perms' => array( |
||||
2328 | 'chmod' => @is_writable($path . '/' . $folder), |
||||
2329 | 'perms' => @fileperms($path . '/' . $folder), |
||||
2330 | ), |
||||
2331 | ); |
||||
2332 | if ($type !== true) |
||||
2333 | $additional_data['type'] = $type; |
||||
2334 | |||||
2335 | // If there's an offset ignore any folders in XML mode. |
||||
2336 | if (isset($_GET['xml']) && $context['file_offset'] == 0) |
||||
2337 | { |
||||
2338 | $context['xml_data']['folders']['children'][] = array( |
||||
2339 | 'attributes' => array( |
||||
2340 | 'writable' => $additional_data['perms']['chmod'] ? 1 : 0, |
||||
2341 | 'permissions' => substr(sprintf('%o', $additional_data['perms']['perms']), -4), |
||||
2342 | 'folder' => 1, |
||||
2343 | 'path' => $context['only_find'], |
||||
2344 | 'level' => $level, |
||||
2345 | 'more' => 0, |
||||
2346 | 'offset' => $context['file_offset'], |
||||
2347 | 'my_ident' => preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $context['only_find'] . '/' . $folder), |
||||
2348 | 'ident' => preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $context['only_find']), |
||||
2349 | ), |
||||
2350 | 'value' => $folder, |
||||
2351 | ); |
||||
2352 | } |
||||
2353 | elseif (!isset($_GET['xml'])) |
||||
2354 | { |
||||
2355 | if (isset($data['contents'][$folder])) |
||||
2356 | $data['contents'][$folder] = array_merge($data['contents'][$folder], $additional_data); |
||||
2357 | else |
||||
2358 | $data['contents'][$folder] = $additional_data; |
||||
2359 | } |
||||
2360 | } |
||||
2361 | |||||
2362 | // Now we want to do a similar thing with files. |
||||
2363 | ksort($foundData['files']); |
||||
2364 | $counter = -1; |
||||
2365 | foreach ($foundData['files'] as $file => $dummy) |
||||
2366 | { |
||||
2367 | $counter++; |
||||
2368 | |||||
2369 | // Have we reached our offset? |
||||
2370 | if ($context['file_offset'] > $counter) |
||||
2371 | continue; |
||||
2372 | // Gone too far? |
||||
2373 | if ($counter > ($context['file_offset'] + $context['file_limit'])) |
||||
2374 | continue; |
||||
2375 | |||||
2376 | $additional_data = array( |
||||
2377 | 'perms' => array( |
||||
2378 | 'chmod' => @is_writable($path . '/' . $file), |
||||
2379 | 'perms' => @fileperms($path . '/' . $file), |
||||
2380 | ), |
||||
2381 | ); |
||||
2382 | |||||
2383 | // XML? |
||||
2384 | if (isset($_GET['xml'])) |
||||
2385 | { |
||||
2386 | $context['xml_data']['folders']['children'][] = array( |
||||
2387 | 'attributes' => array( |
||||
2388 | 'writable' => $additional_data['perms']['chmod'] ? 1 : 0, |
||||
2389 | 'permissions' => substr(sprintf('%o', $additional_data['perms']['perms']), -4), |
||||
2390 | 'folder' => 0, |
||||
2391 | 'path' => $context['only_find'], |
||||
2392 | 'level' => $level, |
||||
2393 | 'more' => $counter == ($context['file_offset'] + $context['file_limit']) ? 1 : 0, |
||||
2394 | 'offset' => $context['file_offset'], |
||||
2395 | 'my_ident' => preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $context['only_find'] . '/' . $file), |
||||
2396 | 'ident' => preg_replace('~[^A-Za-z0-9_\-=:]~', ':-:', $context['only_find']), |
||||
2397 | ), |
||||
2398 | 'value' => $file, |
||||
2399 | ); |
||||
2400 | } |
||||
2401 | elseif ($counter != ($context['file_offset'] + $context['file_limit'])) |
||||
2402 | { |
||||
2403 | if (isset($data['contents'][$file])) |
||||
2404 | $data['contents'][$file] = array_merge($data['contents'][$file], $additional_data); |
||||
2405 | else |
||||
2406 | $data['contents'][$file] = $additional_data; |
||||
2407 | } |
||||
2408 | } |
||||
2409 | } |
||||
2410 | |||||
2411 | /** |
||||
2412 | * Actually action the permission changes they want. |
||||
2413 | */ |
||||
2414 | function PackagePermissionsAction() |
||||
2415 | { |
||||
2416 | global $smcFunc, $context, $txt, $time_start, $package_ftp; |
||||
2417 | |||||
2418 | umask(0); |
||||
2419 | |||||
2420 | $timeout_limit = 5; |
||||
2421 | |||||
2422 | $context['method'] = $_POST['method'] == 'individual' ? 'individual' : 'predefined'; |
||||
2423 | $context['sub_template'] = 'action_permissions'; |
||||
2424 | $context['page_title'] = $txt['package_file_perms_applying']; |
||||
2425 | $context['back_look_data'] = isset($_POST['back_look']) ? $_POST['back_look'] : array(); |
||||
2426 | |||||
2427 | // Skipping use of FTP? |
||||
2428 | if (empty($package_ftp)) |
||||
2429 | $context['skip_ftp'] = true; |
||||
2430 | |||||
2431 | // We'll start off in a good place, security. Make sure that if we're dealing with individual files that they seem in the right place. |
||||
2432 | if ($context['method'] == 'individual') |
||||
2433 | { |
||||
2434 | // Only these path roots are legal. |
||||
2435 | $legal_roots = array_keys($context['file_tree']); |
||||
2436 | $context['custom_value'] = (int) $_POST['custom_value']; |
||||
2437 | |||||
2438 | // Continuing? |
||||
2439 | if (isset($_POST['toProcess'])) |
||||
2440 | $_POST['permStatus'] = $smcFunc['json_decode'](base64_decode($_POST['toProcess']), true); |
||||
2441 | |||||
2442 | if (isset($_POST['permStatus'])) |
||||
2443 | { |
||||
2444 | $context['to_process'] = array(); |
||||
2445 | $validate_custom = false; |
||||
2446 | foreach ($_POST['permStatus'] as $path => $status) |
||||
2447 | { |
||||
2448 | // Nothing to see here? |
||||
2449 | if ($status == 'no_change') |
||||
2450 | continue; |
||||
2451 | $legal = false; |
||||
2452 | foreach ($legal_roots as $root) |
||||
2453 | if (substr($path, 0, strlen($root)) == $root) |
||||
2454 | $legal = true; |
||||
2455 | |||||
2456 | if (!$legal) |
||||
2457 | continue; |
||||
2458 | |||||
2459 | // Check it exists. |
||||
2460 | if (!file_exists($path)) |
||||
2461 | continue; |
||||
2462 | |||||
2463 | if ($status == 'custom') |
||||
2464 | $validate_custom = true; |
||||
2465 | |||||
2466 | // Now add it. |
||||
2467 | $context['to_process'][$path] = $status; |
||||
2468 | } |
||||
2469 | $context['total_items'] = isset($_POST['totalItems']) ? (int) $_POST['totalItems'] : count($context['to_process']); |
||||
2470 | |||||
2471 | // Make sure the chmod status is valid? |
||||
2472 | if ($validate_custom) |
||||
2473 | { |
||||
2474 | if (preg_match('~^[4567][4567][4567]$~', $context['custom_value']) == false) |
||||
0 ignored issues
–
show
|
|||||
2475 | fatal_error($txt['chmod_value_invalid']); |
||||
2476 | } |
||||
2477 | |||||
2478 | // Nothing to do? |
||||
2479 | if (empty($context['to_process'])) |
||||
2480 | redirectexit('action=admin;area=packages;sa=perms' . (!empty($context['back_look_data']) ? ';back_look=' . base64_encode($smcFunc['json_encode']($context['back_look_data'])) : '') . ';' . $context['session_var'] . '=' . $context['session_id']); |
||||
2481 | } |
||||
2482 | // Should never get here, |
||||
2483 | else |
||||
2484 | fatal_lang_error('no_access', false); |
||||
2485 | |||||
2486 | // Setup the custom value. |
||||
2487 | $custom_value = octdec('0' . $context['custom_value']); |
||||
2488 | |||||
2489 | // Start processing items. |
||||
2490 | foreach ($context['to_process'] as $path => $status) |
||||
2491 | { |
||||
2492 | if (in_array($status, array('execute', 'writable', 'read'))) |
||||
2493 | package_chmod($path, $status); |
||||
2494 | elseif ($status == 'custom' && !empty($custom_value)) |
||||
2495 | { |
||||
2496 | // Use FTP if we have it. |
||||
2497 | if (!empty($package_ftp) && !empty($_SESSION['pack_ftp'])) |
||||
2498 | { |
||||
2499 | $ftp_file = strtr($path, array($_SESSION['pack_ftp']['root'] => '')); |
||||
2500 | $package_ftp->chmod($ftp_file, $custom_value); |
||||
2501 | } |
||||
2502 | else |
||||
2503 | smf_chmod($path, $custom_value); |
||||
2504 | } |
||||
2505 | |||||
2506 | // This fish is fried... |
||||
2507 | unset($context['to_process'][$path]); |
||||
2508 | |||||
2509 | // See if we're out of time? |
||||
2510 | if ((time() - $time_start) > $timeout_limit) |
||||
2511 | { |
||||
2512 | // Prepare template usage for to_process. |
||||
2513 | $context['to_process_encode'] = base64_encode($smcFunc['json_encode']($context['to_process'])); |
||||
2514 | |||||
2515 | return false; |
||||
2516 | } |
||||
2517 | } |
||||
2518 | |||||
2519 | // Prepare template usage for to_process. |
||||
2520 | $context['to_process_encode'] = base64_encode($smcFunc['json_encode']($context['to_process'])); |
||||
2521 | } |
||||
2522 | // If predefined this is a little different. |
||||
2523 | else |
||||
2524 | { |
||||
2525 | $context['predefined_type'] = isset($_POST['predefined']) ? $_POST['predefined'] : 'restricted'; |
||||
2526 | |||||
2527 | $context['total_items'] = isset($_POST['totalItems']) ? (int) $_POST['totalItems'] : 0; |
||||
2528 | $context['directory_list'] = isset($_POST['dirList']) ? $smcFunc['json_decode'](base64_decode($_POST['dirList']), true) : array(); |
||||
2529 | |||||
2530 | $context['file_offset'] = isset($_POST['fileOffset']) ? (int) $_POST['fileOffset'] : 0; |
||||
2531 | |||||
2532 | // Haven't counted the items yet? |
||||
2533 | if (empty($context['total_items'])) |
||||
2534 | { |
||||
2535 | /** |
||||
2536 | * Counts all the directories under a given path |
||||
2537 | * |
||||
2538 | * @param string $dir |
||||
2539 | * @return integer |
||||
2540 | */ |
||||
2541 | function count_directories__recursive($dir) |
||||
2542 | { |
||||
2543 | global $context; |
||||
2544 | |||||
2545 | $count = 0; |
||||
2546 | $dh = @opendir($dir); |
||||
2547 | while ($entry = readdir($dh)) |
||||
0 ignored issues
–
show
It seems like
$dh can also be of type false ; however, parameter $dir_handle of readdir() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
2548 | { |
||||
2549 | if ($entry != '.' && $entry != '..' && is_dir($dir . '/' . $entry)) |
||||
2550 | { |
||||
2551 | $context['directory_list'][$dir . '/' . $entry] = 1; |
||||
2552 | $count++; |
||||
2553 | $count += count_directories__recursive($dir . '/' . $entry); |
||||
2554 | } |
||||
2555 | } |
||||
2556 | closedir($dh); |
||||
0 ignored issues
–
show
It seems like
$dh can also be of type false ; however, parameter $dir_handle of closedir() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
2557 | |||||
2558 | return $count; |
||||
2559 | } |
||||
2560 | |||||
2561 | foreach ($context['file_tree'] as $path => $data) |
||||
2562 | { |
||||
2563 | if (is_dir($path)) |
||||
2564 | { |
||||
2565 | $context['directory_list'][$path] = 1; |
||||
2566 | $context['total_items'] += count_directories__recursive($path); |
||||
2567 | $context['total_items']++; |
||||
2568 | } |
||||
2569 | } |
||||
2570 | } |
||||
2571 | |||||
2572 | // Have we built up our list of special files? |
||||
2573 | if (!isset($_POST['specialFiles']) && $context['predefined_type'] != 'free') |
||||
2574 | { |
||||
2575 | $context['special_files'] = array(); |
||||
2576 | |||||
2577 | /** |
||||
2578 | * Builds a list of special files recursively for a given path |
||||
2579 | * |
||||
2580 | * @param string $path |
||||
2581 | * @param array $data |
||||
2582 | */ |
||||
2583 | function build_special_files__recursive($path, &$data) |
||||
2584 | { |
||||
2585 | global $context; |
||||
2586 | |||||
2587 | if (!empty($data['writable_on'])) |
||||
2588 | if ($context['predefined_type'] == 'standard' || $data['writable_on'] == 'restrictive') |
||||
2589 | $context['special_files'][$path] = 1; |
||||
2590 | |||||
2591 | if (!empty($data['contents'])) |
||||
2592 | foreach ($data['contents'] as $name => $contents) |
||||
2593 | build_special_files__recursive($path . '/' . $name, $contents); |
||||
2594 | } |
||||
2595 | |||||
2596 | foreach ($context['file_tree'] as $path => $data) |
||||
2597 | build_special_files__recursive($path, $data); |
||||
2598 | } |
||||
2599 | // Free doesn't need special files. |
||||
2600 | elseif ($context['predefined_type'] == 'free') |
||||
2601 | $context['special_files'] = array(); |
||||
2602 | else |
||||
2603 | $context['special_files'] = $smcFunc['json_decode'](base64_decode($_POST['specialFiles']), true); |
||||
2604 | |||||
2605 | // Now we definitely know where we are, we need to go through again doing the chmod! |
||||
2606 | foreach ($context['directory_list'] as $path => $dummy) |
||||
2607 | { |
||||
2608 | // Do the contents of the directory first. |
||||
2609 | $dh = @opendir($path); |
||||
2610 | $file_count = 0; |
||||
2611 | $dont_chmod = false; |
||||
2612 | while ($entry = readdir($dh)) |
||||
0 ignored issues
–
show
It seems like
$dh can also be of type false ; however, parameter $dir_handle of readdir() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
2613 | { |
||||
2614 | $file_count++; |
||||
2615 | // Actually process this file? |
||||
2616 | if (!$dont_chmod && !is_dir($path . '/' . $entry) && (empty($context['file_offset']) || $context['file_offset'] < $file_count)) |
||||
2617 | { |
||||
2618 | $status = $context['predefined_type'] == 'free' || isset($context['special_files'][$path . '/' . $entry]) ? 'writable' : 'execute'; |
||||
2619 | package_chmod($path . '/' . $entry, $status); |
||||
2620 | } |
||||
2621 | |||||
2622 | // See if we're out of time? |
||||
2623 | if (!$dont_chmod && (time() - $time_start) > $timeout_limit) |
||||
2624 | { |
||||
2625 | $dont_chmod = true; |
||||
2626 | // Don't do this again. |
||||
2627 | $context['file_offset'] = $file_count; |
||||
2628 | } |
||||
2629 | } |
||||
2630 | closedir($dh); |
||||
0 ignored issues
–
show
It seems like
$dh can also be of type false ; however, parameter $dir_handle of closedir() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
2631 | |||||
2632 | // If this is set it means we timed out half way through. |
||||
2633 | if ($dont_chmod) |
||||
2634 | { |
||||
2635 | $context['total_files'] = $file_count; |
||||
2636 | $context['directory_list_encode'] = base64_encode($smcFunc['json_encode']($context['directory_list'])); |
||||
2637 | $context['special_files_encode'] = base64_encode($smcFunc['json_encode']($context['special_files'])); |
||||
2638 | return false; |
||||
2639 | } |
||||
2640 | |||||
2641 | // Do the actual directory. |
||||
2642 | $status = $context['predefined_type'] == 'free' || isset($context['special_files'][$path]) ? 'writable' : 'execute'; |
||||
2643 | package_chmod($path, $status); |
||||
2644 | |||||
2645 | // We've finished the directory so no file offset, and no record. |
||||
2646 | $context['file_offset'] = 0; |
||||
2647 | unset($context['directory_list'][$path]); |
||||
2648 | |||||
2649 | // See if we're out of time? |
||||
2650 | if ((time() - $time_start) > $timeout_limit) |
||||
2651 | { |
||||
2652 | // Prepare this for usage on templates. |
||||
2653 | $context['directory_list_encode'] = base64_encode($smcFunc['json_encode']($context['directory_list'])); |
||||
2654 | $context['special_files_encode'] = base64_encode($smcFunc['json_encode']($context['special_files'])); |
||||
2655 | |||||
2656 | return false; |
||||
2657 | } |
||||
2658 | } |
||||
2659 | |||||
2660 | // Prepare this for usage on templates. |
||||
2661 | $context['directory_list_encode'] = base64_encode($smcFunc['json_encode']($context['directory_list'])); |
||||
2662 | $context['special_files_encode'] = base64_encode($smcFunc['json_encode']($context['special_files'])); |
||||
2663 | } |
||||
2664 | |||||
2665 | // If we're here we are done! |
||||
2666 | redirectexit('action=admin;area=packages;sa=perms' . (!empty($context['back_look_data']) ? ';back_look=' . base64_encode($smcFunc['json_encode']($context['back_look_data'])) : '') . ';' . $context['session_var'] . '=' . $context['session_id']); |
||||
2667 | } |
||||
2668 | |||||
2669 | /** |
||||
2670 | * Test an FTP connection. |
||||
2671 | */ |
||||
2672 | function PackageFTPTest() |
||||
2673 | { |
||||
2674 | global $context, $txt, $package_ftp; |
||||
2675 | |||||
2676 | checkSession('get'); |
||||
2677 | |||||
2678 | // Try to make the FTP connection. |
||||
2679 | create_chmod_control(array(), array('force_find_error' => true)); |
||||
2680 | |||||
2681 | // Deal with the template stuff. |
||||
2682 | loadTemplate('Xml'); |
||||
2683 | $context['sub_template'] = 'generic_xml'; |
||||
2684 | $context['template_layers'] = array(); |
||||
2685 | |||||
2686 | // Define the return data, this is simple. |
||||
2687 | $context['xml_data'] = array( |
||||
2688 | 'results' => array( |
||||
2689 | 'identifier' => 'result', |
||||
2690 | 'children' => array( |
||||
2691 | array( |
||||
2692 | 'attributes' => array( |
||||
2693 | 'success' => !empty($package_ftp) ? 1 : 0, |
||||
2694 | ), |
||||
2695 | 'value' => !empty($package_ftp) ? $txt['package_ftp_test_success'] : (isset($context['package_ftp'], $context['package_ftp']['error']) ? $context['package_ftp']['error'] : $txt['package_ftp_test_failed']), |
||||
2696 | ), |
||||
2697 | ), |
||||
2698 | ), |
||||
2699 | ); |
||||
2700 | } |
||||
2701 | |||||
2702 | ?> |