/** * List operations */ public function action_showoperations() { global $context, $txt; // Can't be in here buddy. isAllowedTo('admin_forum'); // We need to know the operation key for the search and replace, mod file looking at, is it a board mod? if (!isset($_REQUEST['operation_key'], $_REQUEST['filename']) && !is_numeric($_REQUEST['operation_key'])) { fatal_lang_error('operation_invalid', 'general'); } // Load the required file. require_once SUBSDIR . '/Package.subs.php'; require_once SUBSDIR . '/Themes.subs.php'; // Uninstalling the mod? $reverse = isset($_REQUEST['reverse']) ? true : false; // Get the base name. $context['filename'] = preg_replace('~[\\.]+~', '.', $_REQUEST['package']); // We need to extract this again. if (is_file(BOARDDIR . '/packages/' . $context['filename'])) { $context['extracted_files'] = read_tgz_file(BOARDDIR . '/packages/' . $context['filename'], BOARDDIR . '/packages/temp'); if ($context['extracted_files'] && !file_exists(BOARDDIR . '/packages/temp/package-info.xml')) { foreach ($context['extracted_files'] as $file) { if (basename($file['filename']) == 'package-info.xml') { $context['base_path'] = dirname($file['filename']) . '/'; break; } } } if (!isset($context['base_path'])) { $context['base_path'] = ''; } } elseif (is_dir(BOARDDIR . '/packages/' . $context['filename'])) { copytree(BOARDDIR . '/packages/' . $context['filename'], BOARDDIR . '/packages/temp'); $context['extracted_files'] = listtree(BOARDDIR . '/packages/temp'); $context['base_path'] = ''; } // Load up any custom themes we may want to install into... $theme_paths = getThemesPathbyID(); // For uninstall operations we only consider the themes in which the package is installed. if (isset($_REQUEST['reverse']) && !empty($_REQUEST['install_id'])) { $install_id = (int) $_REQUEST['install_id']; if ($install_id > 0) { $old_themes = loadThemesAffected($install_id); foreach ($theme_paths as $id => $data) { if ($id != 1 && !in_array($id, $old_themes)) { unset($theme_paths[$id]); } } } } // Boardmod? if (isset($_REQUEST['boardmod'])) { $mod_actions = parseBoardMod(@file_get_contents(BOARDDIR . '/packages/temp/' . $context['base_path'] . $_REQUEST['filename']), true, $reverse, $theme_paths); } else { $mod_actions = parseModification(@file_get_contents(BOARDDIR . '/packages/temp/' . $context['base_path'] . $_REQUEST['filename']), true, $reverse, $theme_paths); } // Ok lets get the content of the file. $context['operations'] = array('search' => strtr(htmlspecialchars($mod_actions[$_REQUEST['operation_key']]['search_original'], ENT_COMPAT, 'UTF-8'), array('[' => '[', ']' => ']')), 'replace' => strtr(htmlspecialchars($mod_actions[$_REQUEST['operation_key']]['replace_original'], ENT_COMPAT, 'UTF-8'), array('[' => '[', ']' => ']')), 'position' => $mod_actions[$_REQUEST['operation_key']]['position']); // Let's do some formatting... $operation_text = $context['operations']['position'] == 'replace' ? 'operation_replace' : ($context['operations']['position'] == 'before' ? 'operation_after' : 'operation_before'); $context['operations']['search'] = parse_bbc('[code=' . $txt['operation_find'] . ']' . ($context['operations']['position'] == 'end' ? '?>' : $context['operations']['search']) . '[/code]'); $context['operations']['replace'] = parse_bbc('[code=' . $txt[$operation_text] . ']' . $context['operations']['replace'] . '[/code]'); // No layers Template_Layers::getInstance()->removeAll(); $context['sub_template'] = 'view_operations'; }
function PackageInstall() { global $boarddir, $txt, $context, $boardurl, $scripturl, $sourcedir, $modSettings; checkSession('post'); // If there's no file, what are we installing? if (empty($_REQUEST['package']) || preg_match('~[^\\w0-9.\\-_]~', $_REQUEST['package']) === 1 || strpos($_REQUEST['package'], '..') !== false) { redirectexit('action=packages'); } $context['filename'] = $_REQUEST['package']; require_once $sourcedir . '/Subs-Package.php'; // !!! TODO: Perhaps do it in steps, if necessary? $context['uninstalling'] = $_REQUEST['sa'] == 'uninstall2'; // Set up the linktree for other. $context['linktree'][] = array('url' => $scripturl . '?action=packages;sa=browse', 'name' => $context['uninstalling'] ? $txt['smf198b'] : $txt['package37']); $context['page_title'] .= ' - ' . ($context['uninstalling'] ? $txt['smf198b'] : $txt['package37']); $context['sub_template'] = 'extract_package'; if (!file_exists($boarddir . '/Packages/' . $context['filename'])) { fatal_lang_error('package_no_file', false); } // Load up the package FTP information? if (isset($_SESSION['pack_ftp'])) { packageRequireFTP($scripturl . '?action=packages;sa=' . $_REQUEST['sa'] . ';package=' . $_REQUEST['package']); } // Make sure temp directory exists and is empty! if (file_exists($boarddir . '/Packages/temp')) { deltree($boarddir . '/Packages/temp', false); } else { mktree($boarddir . '/Packages/temp', 0777); } // Let the unpacker do the work. if (is_file($boarddir . '/Packages/' . $context['filename'])) { $context['extracted_files'] = read_tgz_file($boarddir . '/Packages/' . $context['filename'], $boarddir . '/Packages/temp'); if (!file_exists($boarddir . '/Packages/temp/package-info.xml')) { foreach ($context['extracted_files'] as $file) { if (basename($file['filename']) == 'package-info.xml') { $context['base_path'] = dirname($file['filename']) . '/'; break; } } } if (!isset($context['base_path'])) { $context['base_path'] = ''; } } elseif (is_dir($boarddir . '/Packages/' . $context['filename'])) { copytree($boarddir . '/Packages/' . $context['filename'], $boarddir . '/Packages/temp'); $context['extracted_files'] = listtree($boarddir . '/Packages/temp'); $context['base_path'] = ''; } else { fatal_lang_error(1, false); } // Get the package info... $packageInfo = getPackageInfo($context['filename']); $packageInfo['filename'] = $context['filename']; // Set the type of extraction... $context['extract_type'] = isset($packageInfo['type']) ? $packageInfo['type'] : 'modification'; // Create a backup file to roll back to! (but if they do this more than once, don't run it a zillion times.) if (!empty($modSettings['package_make_backups']) && (!isset($_SESSION['last_backup_for']) || $_SESSION['last_backup_for'] != $context['filename'] . ($context['uninstalling'] ? '$$' : '$'))) { $_SESSION['last_backup_for'] = $context['filename'] . ($context['uninstalling'] ? '$$' : '$'); // !!! Internationalize this? package_create_backup(($context['uninstalling'] ? 'backup_' : 'before_') . strtok($context['filename'], '.')); } $instmods = loadInstalledPackages(); // The mod isn't installed.... unless proven otherwise. $context['is_installed'] = false; foreach ($instmods as $installed_mod) { if ($installed_mod['id'] == $packageInfo['id']) { $old_version = $installed_mod['version']; } } // Wait, it's not installed yet! // !!! TODO: Replace with a better error message! if (!isset($old_version) && $context['uninstalling']) { deltree($boarddir . '/Packages/temp'); fatal_error('Hacker?', false); } elseif ($context['uninstalling']) { $install_log = parsePackageInfo($packageInfo['xml'], false, 'uninstall'); // Gadzooks! There's no uninstaller at all!? if (empty($install_log)) { fatal_lang_error('package_uninstall_cannot', false); } } elseif (isset($old_version) && $old_version != $packageInfo['version']) { // Look for an upgrade... $install_log = parsePackageInfo($packageInfo['xml'], false, 'upgrade', $old_version); // There was no upgrade.... if (empty($install_log)) { $context['is_installed'] = true; } } elseif (isset($old_version) && $old_version == $packageInfo['version']) { $context['is_installed'] = true; } if (!isset($old_version) || $context['is_installed']) { $install_log = parsePackageInfo($packageInfo['xml'], false, 'install'); } $context['install_finished'] = false; // !!! TODO: Make a log of any errors that occurred and output them? if (!empty($install_log)) { foreach ($install_log as $action) { if ($action['type'] == 'modification' && !empty($action['filename'])) { if ($action['boardmod']) { parseBoardMod(file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']), false, $action['reverse']); } else { parseModification(file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']), false, $action['reverse']); } } elseif ($action['type'] == 'code' && !empty($action['filename'])) { // This is just here as reference for what is available. global $txt, $boarddir, $sourcedir, $modSettings, $context, $settings, $db_prefix, $forum_version; // Now include the file and be done with it ;). require $boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']; } elseif ($action['type'] == 'redirect' && !empty($action['redirect_url'])) { $context['redirect_url'] = $action['redirect_url']; $context['redirect_text'] = file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']); $context['redirect_timeout'] = $action['redirect_timeout']; // Parse out a couple of common urls. $urls = array('$boardurl' => $boardurl, '$scripturl' => $scripturl, '$session_id' => $context['session_id']); $context['redirect_url'] = strtr($context['redirect_url'], $urls); } } package_flush_cache(); // Check if the mod has been installed. $seen = false; // Look through the list of installed mods... foreach ($instmods as $i => $installed_mod) { if ($installed_mod['id'] == $packageInfo['id']) { if ($context['uninstalling']) { $instmods[$i] = array(); } else { $instmods[$i]['version'] = $packageInfo['version']; $seen = true; } break; } } // Hasn't.... make it show as installed. if (!$seen && !$context['uninstalling']) { $instmods[] = $packageInfo; } saveInstalledPackages($instmods); $context['install_finished'] = true; } // Clean house... get rid of the evidence ;). if (file_exists($boarddir . '/Packages/temp')) { deltree($boarddir . '/Packages/temp'); } }
function ViewOperations() { global $context, $txt, $boarddir, $sourcedir, $modSettings; // Can't be in here buddy. isAllowedTo('admin_forum'); // We need to know the operation key for the search and replace, mod file looking at, is it a board mod? if (!isset($_REQUEST['operation_key'], $_REQUEST['filename']) && !is_numeric($_REQUEST['operation_key'])) { fatal_lang_error('operation_invalid', 'general'); } // Load the required file. require_once $sourcedir . '/lib/Subs-Package.php'; // Uninstalling the mod? $reverse = isset($_REQUEST['reverse']) ? true : false; // Get the base name. $context['filename'] = preg_replace('~[\\.]+~', '.', $_REQUEST['package']); // We need to extract this again. if (is_file($boarddir . '/Packages/' . $context['filename'])) { $context['extracted_files'] = read_tgz_file($boarddir . '/Packages/' . $context['filename'], $boarddir . '/Packages/temp'); if ($context['extracted_files'] && !file_exists($boarddir . '/Packages/temp/package-info.xml')) { foreach ($context['extracted_files'] as $file) { if (basename($file['filename']) == 'package-info.xml') { $context['base_path'] = dirname($file['filename']) . '/'; break; } } } if (!isset($context['base_path'])) { $context['base_path'] = ''; } } elseif (is_dir($boarddir . '/Packages/' . $context['filename'])) { copytree($boarddir . '/Packages/' . $context['filename'], $boarddir . '/Packages/temp'); $context['extracted_files'] = listtree($boarddir . '/Packages/temp'); $context['base_path'] = ''; } // Load up any custom themes we may want to install into... $request = smf_db_query(' SELECT id_theme, variable, value FROM {db_prefix}themes WHERE (id_theme = {int:default_theme} OR id_theme IN ({array_int:known_theme_list})) AND variable IN ({string:name}, {string:theme_dir})', array('known_theme_list' => explode(',', $modSettings['knownThemes']), 'default_theme' => 1, 'name' => 'name', 'theme_dir' => 'theme_dir')); $theme_paths = array(); while ($row = mysql_fetch_assoc($request)) { $theme_paths[$row['id_theme']][$row['variable']] = $row['value']; } mysql_free_result($request); // Boardmod? if (isset($_REQUEST['boardmod'])) { $mod_actions = parseBoardMod(@file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $_REQUEST['filename']), true, $reverse, $theme_paths); } else { $mod_actions = parseModification(@file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $_REQUEST['filename']), true, $reverse, $theme_paths); } // Ok lets get the content of the file. $context['operations'] = array('search' => strtr(htmlspecialchars($mod_actions[$_REQUEST['operation_key']]['search_original']), array('[' => '[', ']' => ']')), 'replace' => strtr(htmlspecialchars($mod_actions[$_REQUEST['operation_key']]['replace_original']), array('[' => '[', ']' => ']')), 'position' => $mod_actions[$_REQUEST['operation_key']]['position']); // Let's do some formatting... $operation_text = $context['operations']['position'] == 'replace' ? 'operation_replace' : ($context['operations']['position'] == 'before' ? 'operation_after' : 'operation_before'); $context['operations']['search'] = parse_bbc('[code=' . $txt['operation_find'] . ']' . ($context['operations']['position'] == 'end' ? '?>' : $context['operations']['search']) . '[/code]'); $context['operations']['replace'] = parse_bbc('[code=' . $txt[$operation_text] . ']' . $context['operations']['replace'] . '[/code]'); // No layers $context['template_layers'] = array(); $context['sub_template'] = 'view_operations'; }
function CleanupMods() { global $db_prefix, $modSettings, $upcontext, $boarddir, $sourcedir, $settings, $smcFunc, $command_line; // Sorry. Not supported for command line users. if ($command_line) { return true; } // Skipping first? if (!empty($_POST['skip'])) { unset($_POST['skip']); return true; } // If we get here withOUT SSI we need to redirect to ensure we get it! if (!isset($_GET['ssi']) || !function_exists('mktree')) { redirectLocation('&ssi=1'); } $upcontext['sub_template'] = 'clean_mods'; $upcontext['page_title'] = 'Cleanup Modifications'; // This can be skipped. $upcontext['skip'] = true; // If we're on the second redirect continue... if (isset($_POST['cleandone2'])) { return true; } // Do we already know about some writable files? if (isset($_POST['writable_files'])) { $writable_files = unserialize(base64_decode($_POST['writable_files'])); if (!makeFilesWritable($writable_files)) { // What have we left? $upcontext['writable_files'] = $writable_files; return false; } } // Load all theme paths.... $request = $smcFunc['db_query']('', ' SELECT id_theme, variable, value FROM {db_prefix}themes WHERE id_member = {int:id_member} AND variable IN ({string:theme_dir}, {string:images_url})', array('id_member' => 0, 'theme_dir' => 'theme_dir', 'images_url' => 'images_url', 'db_error_skip' => true)); $theme_paths = array(); while ($row = $smcFunc['db_fetch_assoc']($request)) { if ($row['id_theme'] == 1) { $settings['default_' . $row['variable']] = $row['value']; } elseif ($row['variable'] == 'theme_dir') { $theme_paths[$row['id_theme']][$row['variable']] = $row['value']; } } $smcFunc['db_free_result']($request); // Are there are mods installed that may need uninstalling? $request = $smcFunc['db_query']('', ' SELECT id_install, filename, name, themes_installed, version FROM {db_prefix}log_packages WHERE install_state = {int:installed} ORDER BY time_installed DESC', array('installed' => 1, 'db_error_skip' => true)); $upcontext['packages'] = array(); while ($row = $smcFunc['db_fetch_assoc']($request)) { // Work out the status. if (!file_exists($boarddir . '/Packages/' . $row['filename'])) { $status = 'Missing'; $status_color = 'red'; $result = 'Removed'; } else { $status = 'Installed'; $status_color = 'green'; $result = 'No Action Needed'; } $upcontext['packages'][$row['id_install']] = array('id' => $row['id_install'], 'themes' => explode(',', $row['themes_installed']), 'name' => $row['name'], 'filename' => $row['filename'], 'missing_file' => file_exists($boarddir . '/Packages/' . $row['filename']) ? 0 : 1, 'files' => array(), 'file_count' => 0, 'status' => $status, 'result' => $result, 'color' => $status_color, 'version' => $row['version'], 'needs_removing' => false); } $smcFunc['db_free_result']($request); // Don't carry on if there are none. if (empty($upcontext['packages'])) { return true; } // Setup some basics. if (!empty($upcontext['user']['version'])) { $_SESSION['version_emulate'] = $upcontext['user']['version']; } // Before we get started, don't report notice errors. $oldErrorReporting = error_reporting(E_ALL ^ E_NOTICE); if (!mktree($boarddir . '/Packages/temp', 0755)) { deltree($boarddir . '/Packages/temp', false); if (!mktree($boarddir . '/Packages/temp', 0777)) { deltree($boarddir . '/Packages/temp', false); //!!! Error here - plus chmod! } } // Anything which reinstalled should not have its entry removed. $reinstall_worked = array(); // We're gonna be doing some removin' $test = isset($_POST['cleandone']) ? false : true; foreach ($upcontext['packages'] as $id => $package) { // Can't do anything about this.... if ($package['missing_file']) { continue; } // Not testing *and* this wasn't checked? if (!$test && (!isset($_POST['remove']) || !isset($_POST['remove'][$id]))) { continue; } // What are the themes this was installed into? $cur_theme_paths = array(); foreach ($theme_paths as $tid => $data) { if ($tid != 1 && in_array($tid, $package['themes'])) { $cur_theme_paths[$tid] = $data; } } // Get the modifications data if applicable. $filename = $package['filename']; $packageInfo = getPackageInfo($filename); if (!is_array($packageInfo)) { continue; } $info = parsePackageInfo($packageInfo['xml'], $test, 'uninstall'); // Also get the reinstall details... if (isset($_POST['remove'])) { $infoInstall = parsePackageInfo($packageInfo['xml'], true); } if (is_file($boarddir . '/Packages/' . $filename)) { read_tgz_file($boarddir . '/Packages/' . $filename, $boarddir . '/Packages/temp'); } else { copytree($boarddir . '/Packages/' . $filename, $boarddir . '/Packages/temp'); } // Work out how we uninstall... $files = array(); foreach ($info as $change) { // Work out two things: // 1) Whether it's installed at the moment - and if so whether its fully installed, and: // 2) Whether it could be installed on the new version. if ($change['type'] == 'modification') { $contents = @file_get_contents($boarddir . '/Packages/temp/' . $upcontext['base_path'] . $change['filename']); if ($change['boardmod']) { $results = parseBoardMod($contents, $test, $change['reverse'], $cur_theme_paths); } else { $results = parseModification($contents, $test, $change['reverse'], $cur_theme_paths); } foreach ($results as $action) { // Something we can remove? Probably means it existed! if (($action['type'] == 'replace' || $action['type'] == 'append' || !empty($action['filename']) && $action['type'] == 'failure') && !in_array($action['filename'], $files)) { $files[] = $action['filename']; } if ($action['type'] == 'failure') { $upcontext['packages'][$id]['needs_removing'] = true; $upcontext['packages'][$id]['status'] = 'Reinstall Required'; $upcontext['packages'][$id]['color'] = '#FD6435'; } } } } // Store this info for the template as appropriate. $upcontext['packages'][$id]['files'] = $files; $upcontext['packages'][$id]['file_count'] = count($files); // If we've done something save the changes! if (!$test) { package_flush_cache(); } // Are we attempting to reinstall this thing? if (isset($_POST['remove']) && !$test && isset($infoInstall)) { // Need to extract again I'm afraid. if (is_file($boarddir . '/Packages/' . $filename)) { read_tgz_file($boarddir . '/Packages/' . $filename, $boarddir . '/Packages/temp'); } else { copytree($boarddir . '/Packages/' . $filename, $boarddir . '/Packages/temp'); } $errors = false; $upcontext['packages'][$id]['result'] = 'Removed'; foreach ($infoInstall as $change) { if ($change['type'] == 'modification') { $contents = @file_get_contents($boarddir . '/Packages/temp/' . $upcontext['base_path'] . $change['filename']); if ($change['boardmod']) { $results = parseBoardMod($contents, true, $change['reverse'], $cur_theme_paths); } else { $results = parseModification($contents, true, $change['reverse'], $cur_theme_paths); } // Are there any errors? foreach ($results as $action) { if ($action['type'] == 'failure') { $errors = true; } } } } if (!$errors) { $reinstall_worked[] = $id; $upcontext['packages'][$id]['result'] = 'Reinstalled'; $upcontext['packages'][$id]['color'] = 'green'; foreach ($infoInstall as $change) { if ($change['type'] == 'modification') { $contents = @file_get_contents($boarddir . '/Packages/temp/' . $upcontext['base_path'] . $change['filename']); if ($change['boardmod']) { $results = parseBoardMod($contents, false, $change['reverse'], $cur_theme_paths); } else { $results = parseModification($contents, false, $change['reverse'], $cur_theme_paths); } } } // Save the changes. package_flush_cache(); } } } // Put errors back on a sec. error_reporting($oldErrorReporting); // Check everything is writable. if ($test && !empty($upcontext['packages'])) { $writable_files = array(); foreach ($upcontext['packages'] as $package) { if (!empty($package['files'])) { foreach ($package['files'] as $file) { $writable_files[] = $file; } } } if (!empty($writable_files)) { $writable_files = array_unique($writable_files); $upcontext['writable_files'] = $writable_files; if (!makeFilesWritable($writable_files)) { return false; } } } if (file_exists($boarddir . '/Packages/temp')) { deltree($boarddir . '/Packages/temp'); } // Removing/Reinstalling any packages? if (isset($_POST['remove'])) { $deletes = array(); foreach ($_POST['remove'] as $id => $dummy) { if (!in_array((int) $id, $reinstall_worked)) { $deletes[] = (int) $id; } } if (!empty($deletes)) { upgrade_query(' UPDATE ' . $db_prefix . 'log_packages SET install_state = 0 WHERE id_install IN (' . implode(',', $deletes) . ')'); } // Ensure we don't lose our changes! package_put_contents($boarddir . '/Packages/installed.list', time()); $upcontext['sub_template'] = 'cleanup_done'; return false; } else { $allgood = true; // Is there actually anything that needs our attention? foreach ($upcontext['packages'] as $package) { if ($package['color'] != 'green') { $allgood = false; } } if ($allgood) { return true; } } $_GET['substep'] = 0; return isset($_POST['cleandone']) ? true : false; }