function mktree($items, $checked = array(), $admins, $setall = FALSE, $adchild = FALSE)
{
    $html = '';
    $mod = '';
    if (isset($checked[$items['id']]) || $setall) {
        $mod = ' CHECKED ';
    }
    /****
     * If the item has children, we need to create the checkboxes for them by calling the mktree function on each of them
     * If not, just return a single checkbox.
     */
    if (!empty($items['children'])) {
        $html .= '<li class="' . $items['type'] . '">';
        if (trim($items['type']) == 'm' && !$adchild) {
            if (isset($admins[$items['permission']])) {
                $adcheck = 'checked';
            }
            $html .= '<input class="checkbox" type=checkbox name="admin[' . $items['permission'] . ']" value="admin' . $items['id'] . '" ' . $adcheck . ' /> ';
        }
        $html .= '<input class="checkbox" type=checkbox name="permission[' . $items['id'] . ']" value="' . $items['id'] . '"' . $mod . ' /> ' . prettify($items['permission']) . ": " . $items['description'] . '<ul class="permission">';
        foreach ($items['children'] as $child) {
            $html .= mktree($child, $checked, $admins, $setall, TRUE);
        }
        $html .= '</ul></li>';
    } else {
        $html .= '<li class="' . $items['type'] . '">';
        if (trim($items['type']) == 'm' && !$adchild) {
            if (isset($admins[$items['permission']])) {
                $adcheck = 'checked';
            }
            $html .= '<input class="checkbox" type=checkbox name="admin[' . $items['permission'] . ']" value="admin' . $items['id'] . '" ' . $adcheck . ' /> ';
        }
        $html .= '<input class="checkbox"  type=checkbox name="permission[' . $items['id'] . ']" value="' . $items['id'] . '"' . $mod . ' /> ' . prettify($items['permission']) . ': ' . $items['description'] . '</li>';
    }
    return $html;
}
Beispiel #2
0
function package_create_backup($id = 'backup')
{
    global $sourcedir, $boarddir, $smcFunc;
    $files = array();
    $base_files = array('index.php', 'SSI.php', 'agreement.txt', 'ssi_examples.php', 'ssi_examples.shtml');
    foreach ($base_files as $file) {
        if (file_exists($boarddir . '/' . $file)) {
            $files[realpath($boarddir . '/' . $file)] = array(empty($_REQUEST['use_full_paths']) ? $file : $boarddir . '/' . $file, stat($boarddir . '/' . $file));
        }
    }
    $dirs = array($sourcedir => empty($_REQUEST['use_full_paths']) ? 'Sources/' : strtr($sourcedir . '/', '\\', '/'));
    $request = smf_db_query('
		SELECT value
		FROM {db_prefix}themes
		WHERE id_member = {int:no_member}
			AND variable = {string:theme_dir}', array('no_member' => 0, 'theme_dir' => 'theme_dir'));
    while ($row = mysql_fetch_assoc($request)) {
        $dirs[$row['value']] = empty($_REQUEST['use_full_paths']) ? 'Themes/' . basename($row['value']) . '/' : strtr($row['value'] . '/', '\\', '/');
    }
    mysql_free_result($request);
    while (!empty($dirs)) {
        list($dir, $dest) = each($dirs);
        unset($dirs[$dir]);
        $listing = @dir($dir);
        if (!$listing) {
            continue;
        }
        while ($entry = $listing->read()) {
            if (preg_match('~^(\\.{1,2}|CVS|backup.*|help|images|.*\\~)$~', $entry) != 0) {
                continue;
            }
            $filepath = realpath($dir . '/' . $entry);
            if (isset($files[$filepath])) {
                continue;
            }
            $stat = stat($dir . '/' . $entry);
            if ($stat['mode'] & 040000) {
                $files[$filepath] = array($dest . $entry . '/', $stat);
                $dirs[$dir . '/' . $entry] = $dest . $entry . '/';
            } else {
                $files[$filepath] = array($dest . $entry, $stat);
            }
        }
        $listing->close();
    }
    if (!file_exists($boarddir . '/Packages/backups')) {
        mktree($boarddir . '/Packages/backups', 0777);
    }
    if (!is_writable($boarddir . '/Packages/backups')) {
        package_chmod($boarddir . '/Packages/backups');
    }
    $output_file = $boarddir . '/Packages/backups/' . strftime('%Y-%m-%d_') . preg_replace('~[$\\\\/:<>|?*"\']~', '', $id);
    $output_ext = '.tar' . (function_exists('gzopen') ? '.gz' : '');
    if (file_exists($output_file . $output_ext)) {
        $i = 2;
        while (file_exists($output_file . '_' . $i . $output_ext)) {
            $i++;
        }
        $output_file = $output_file . '_' . $i . $output_ext;
    } else {
        $output_file .= $output_ext;
    }
    @set_time_limit(300);
    if (function_exists('apache_reset_timeout')) {
        @apache_reset_timeout();
    }
    if (function_exists('gzopen')) {
        $fwrite = 'gzwrite';
        $fclose = 'gzclose';
        $output = gzopen($output_file, 'wb');
    } else {
        $fwrite = 'fwrite';
        $fclose = 'fclose';
        $output = fopen($output_file, 'wb');
    }
    foreach ($files as $real_file => $file) {
        if (!file_exists($real_file)) {
            continue;
        }
        $stat = $file[1];
        if (substr($file[0], -1) == '/') {
            $stat['size'] = 0;
        }
        $current = pack('a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12', $file[0], decoct($stat['mode']), sprintf('%06d', decoct($stat['uid'])), sprintf('%06d', decoct($stat['gid'])), decoct($stat['size']), decoct($stat['mtime']), '', 0, '', '', '', '', '', '', '', '', '');
        $checksum = 256;
        for ($i = 0; $i < 512; $i++) {
            $checksum += ord($current[$i]);
        }
        $fwrite($output, substr($current, 0, 148) . pack('a8', decoct($checksum)) . substr($current, 156, 511));
        if ($stat['size'] == 0) {
            continue;
        }
        $fp = fopen($real_file, 'rb');
        while (!feof($fp)) {
            $fwrite($output, fread($fp, 16384));
        }
        fclose($fp);
        $fwrite($output, pack('a' . (512 - $stat['size'] % 512), ''));
    }
    $fwrite($output, pack('a1024', ''));
    $fclose($output);
}
 /**
  * Apply another type of (avatar, language, etc.) package.
  */
 public function action_install2()
 {
     global $txt, $context, $boardurl, $scripturl, $modSettings;
     // Make sure we don't install this mod twice.
     checkSubmitOnce('check');
     checkSession();
     // If there's no file, what are we installing?
     if (!isset($_REQUEST['package']) || $_REQUEST['package'] == '') {
         redirectexit('action=admin;area=packages');
     }
     $context['filename'] = $_REQUEST['package'];
     // If this is an uninstall, we'll have an id.
     $context['install_id'] = isset($_REQUEST['pid']) ? (int) $_REQUEST['pid'] : 0;
     require_once SUBSDIR . '/Package.subs.php';
     require_once SUBSDIR . '/Themes.subs.php';
     // @todo Perhaps do it in steps, if necessary?
     $context['uninstalling'] = $_REQUEST['sa'] == 'uninstall2';
     // Set up the linktree for other.
     $context['linktree'][count($context['linktree']) - 1] = array('url' => $scripturl . '?action=admin;area=packages;sa=browse', 'name' => $context['uninstalling'] ? $txt['uninstall'] : $txt['extracting']);
     $context['page_title'] .= ' - ' . ($context['uninstalling'] ? $txt['uninstall'] : $txt['extracting']);
     $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?
     create_chmod_control(array(), array('destination_url' => $scripturl . '?action=admin;area=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('no_access', false);
     }
     // Are we installing this into any custom themes?
     $custom_themes = array(1);
     $known_themes = explode(',', $modSettings['knownThemes']);
     if (!empty($_POST['custom_theme'])) {
         foreach ($_POST['custom_theme'] as $tid) {
             if (in_array($tid, $known_themes)) {
                 $custom_themes[] = (int) $tid;
             }
         }
     }
     // Now load up the paths of the themes that we need to know about.
     $theme_paths = getThemesPathbyID($custom_themes);
     $themes_installed = array(1);
     // Are there any theme copying that we want to take place?
     $context['theme_copies'] = array('require-file' => array(), 'require-dir' => array());
     if (!empty($_POST['theme_changes'])) {
         foreach ($_POST['theme_changes'] as $change) {
             if (empty($change)) {
                 continue;
             }
             $theme_data = unserialize(base64_decode($change));
             if (empty($theme_data['type'])) {
                 continue;
             }
             $themes_installed[] = $theme_data['id'];
             $context['theme_copies'][$theme_data['type']][$theme_data['orig']][] = $theme_data['future'];
         }
     }
     // Get the package info...
     $packageInfo = getPackageInfo($context['filename']);
     if (!is_array($packageInfo)) {
         fatal_lang_error($packageInfo);
     }
     $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_full_backups']) && (!isset($_SESSION['last_backup_for']) || $_SESSION['last_backup_for'] != $context['filename'] . ($context['uninstalling'] ? '$$' : '$'))) {
         $_SESSION['last_backup_for'] = $context['filename'] . ($context['uninstalling'] ? '$$' : '$');
         // @todo Internationalize this?
         package_create_backup(($context['uninstalling'] ? 'backup_' : 'before_') . strtok($context['filename'], '.'));
     }
     // The mod isn't installed.... unless proven otherwise.
     $context['is_installed'] = false;
     // Is it actually installed?
     $package_installed = isPackageInstalled($packageInfo['id']);
     // Wait, it's not installed yet!
     // @todo Replace with a better error message!
     if (!isset($package_installed['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);
         }
         // They can only uninstall from what it was originally installed into.
         foreach ($theme_paths as $id => $data) {
             if ($id != 1 && !in_array($id, $package_installed['old_themes'])) {
                 unset($theme_paths[$id]);
             }
         }
     } elseif (isset($package_installed['old_version']) && $package_installed['old_version'] != $packageInfo['version']) {
         // Look for an upgrade...
         $install_log = parsePackageInfo($packageInfo['xml'], false, 'upgrade', $package_installed['old_version']);
         // There was no upgrade....
         if (empty($install_log)) {
             $context['is_installed'] = true;
         } else {
             // Upgrade previous themes only!
             foreach ($theme_paths as $id => $data) {
                 if ($id != 1 && !in_array($id, $package_installed['old_themes'])) {
                     unset($theme_paths[$id]);
                 }
             }
         }
     } elseif (isset($package_installed['old_version']) && $package_installed['old_version'] == $packageInfo['version']) {
         $context['is_installed'] = true;
     }
     if (!isset($package_installed['old_version']) || $context['is_installed']) {
         $install_log = parsePackageInfo($packageInfo['xml'], false, 'install');
     }
     $context['install_finished'] = false;
     // We're gonna be needing the table db functions! ...Sometimes.
     $table_installer = db_table();
     // @todo Make a log of any errors that occurred and output them?
     if (!empty($install_log)) {
         $failed_steps = array();
         $failed_count = 0;
         foreach ($install_log as $action) {
             $failed_count++;
             if ($action['type'] == 'modification' && !empty($action['filename'])) {
                 if ($action['boardmod']) {
                     $mod_actions = parseBoardMod(file_get_contents(BOARDDIR . '/packages/temp/' . $context['base_path'] . $action['filename']), false, $action['reverse'], $theme_paths);
                 } else {
                     $mod_actions = parseModification(file_get_contents(BOARDDIR . '/packages/temp/' . $context['base_path'] . $action['filename']), false, $action['reverse'], $theme_paths);
                 }
                 // Any errors worth noting?
                 foreach ($mod_actions as $key => $action) {
                     if ($action['type'] == 'failure') {
                         $failed_steps[] = array('file' => $action['filename'], 'large_step' => $failed_count, 'sub_step' => $key, 'theme' => 1);
                     }
                     // Gather the themes we installed into.
                     if (!empty($action['is_custom'])) {
                         $themes_installed[] = $action['is_custom'];
                     }
                 }
             } elseif ($action['type'] == 'code' && !empty($action['filename'])) {
                 // This is just here as reference for what is available.
                 global $txt, $modSettings, $context;
                 // Now include the file and be done with it ;).
                 if (file_exists(BOARDDIR . '/packages/temp/' . $context['base_path'] . $action['filename'])) {
                     require BOARDDIR . '/packages/temp/' . $context['base_path'] . $action['filename'];
                 }
             } elseif ($action['type'] == 'credits') {
                 // Time to build the billboard
                 $credits_tag = array('url' => $action['url'], 'license' => $action['license'], 'copyright' => $action['copyright'], 'title' => $action['title']);
             } elseif ($action['type'] == 'hook' && isset($action['hook'], $action['function'])) {
                 if ($action['reverse']) {
                     remove_integration_function($action['hook'], $action['function'], $action['include_file']);
                 } else {
                     add_integration_function($action['hook'], $action['function'], $action['include_file']);
                 }
             } elseif ($action['type'] == 'database' && !empty($action['filename']) && (!$context['uninstalling'] || !empty($_POST['do_db_changes']))) {
                 // These can also be there for database changes.
                 global $txt, $modSettings, $context;
                 // Let the file work its magic ;)
                 if (file_exists(BOARDDIR . '/packages/temp/' . $context['base_path'] . $action['filename'])) {
                     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'] = !empty($action['filename']) && file_exists(BOARDDIR . '/packages/temp/' . $context['base_path'] . $action['filename']) ? file_get_contents(BOARDDIR . '/packages/temp/' . $context['base_path'] . $action['filename']) : ($context['uninstalling'] ? $txt['package_uninstall_done'] : $txt['package_installed_done']);
                 $context['redirect_timeout'] = $action['redirect_timeout'];
                 // Parse out a couple of common urls.
                 $urls = array('$boardurl' => $boardurl, '$scripturl' => $scripturl, '$session_var' => $context['session_var'], '$session_id' => $context['session_id']);
                 $context['redirect_url'] = strtr($context['redirect_url'], $urls);
             }
         }
         package_flush_cache();
         // First, ensure this change doesn't get removed by putting a stake in the ground (So to speak).
         package_put_contents(BOARDDIR . '/packages/installed.list', time());
         // See if this is already installed
         $is_upgrade = false;
         $old_db_changes = array();
         $package_check = isPackageInstalled($packageInfo['id']);
         // Change the installed state as required.
         if (!empty($package_check['install_state'])) {
             if ($context['uninstalling']) {
                 setPackageState($package_check['package_id']);
             } else {
                 // not uninstalling so must be an upgrade
                 $is_upgrade = true;
                 $old_db_changes = empty($package_check['db_changes']) ? array() : $package_check['db_changes'];
             }
         }
         // Assuming we're not uninstalling, add the entry.
         if (!$context['uninstalling']) {
             // Any db changes from older version?
             $table_log = $table_installer->package_log();
             if (!empty($old_db_changes)) {
                 $db_package_log = empty($table_log) ? $old_db_changes : array_merge($old_db_changes, $table_log);
             } else {
                 $db_package_log = $table_log;
             }
             // If there are some database changes we might want to remove then filter them out.
             if (!empty($db_package_log)) {
                 // We're really just checking for entries which are create table AND add columns (etc).
                 $tables = array();
                 usort($db_package_log, array($this, '_sort_table_first'));
                 foreach ($db_package_log as $k => $log) {
                     if ($log[0] == 'remove_table') {
                         $tables[] = $log[1];
                     } elseif (in_array($log[1], $tables)) {
                         unset($db_package_log[$k]);
                     }
                 }
                 $package_installed['db_changes'] = serialize($db_package_log);
             } else {
                 $package_installed['db_changes'] = '';
             }
             // What themes did we actually install?
             $themes_installed = array_unique($themes_installed);
             $themes_installed = implode(',', $themes_installed);
             // What failed steps?
             $failed_step_insert = serialize($failed_steps);
             // Credits tag?
             $credits_tag = empty($credits_tag) ? '' : serialize($credits_tag);
             // Add to the log packages
             addPackageLog($packageInfo, $failed_step_insert, $themes_installed, $package_installed['db_changes'], $is_upgrade, $credits_tag);
         }
         $context['install_finished'] = true;
     }
     // If there's database changes - and they want them removed - let's do it last!
     if (!empty($package_installed['db_changes']) && !empty($_POST['do_db_changes'])) {
         foreach ($package_installed['db_changes'] as $change) {
             if ($change[0] == 'remove_table' && isset($change[1])) {
                 $table_installer->db_drop_table($change[1]);
             } elseif ($change[0] == 'remove_column' && isset($change[2])) {
                 $table_installer->db_remove_column($change[1], $change[2]);
             } elseif ($change[0] == 'remove_index' && isset($change[2])) {
                 $table_installer->db_remove_index($change[1], $change[2]);
             }
         }
     }
     // Clean house... get rid of the evidence ;).
     if (file_exists(BOARDDIR . '/packages/temp')) {
         deltree(BOARDDIR . '/packages/temp');
     }
     // Log what we just did.
     logAction($context['uninstalling'] ? 'uninstall_package' : (!empty($is_upgrade) ? 'upgrade_package' : 'install_package'), array('package' => Util::htmlspecialchars($packageInfo['name']), 'version' => Util::htmlspecialchars($packageInfo['version'])), 'admin');
     // Just in case, let's clear the whole cache to avoid anything going up the swanny.
     clean_cache();
     // Restore file permissions?
     create_chmod_control(array(), array(), true);
 }
Beispiel #4
0
function PackageInstall()
{
    global $boarddir, $txt, $context, $boardurl, $scripturl, $sourcedir, $modSettings;
    global $user_info, $smcFunc;
    // Make sure we don't install this mod twice.
    checkSubmitOnce('check');
    checkSession();
    // If there's no file, what are we installing?
    if (!isset($_REQUEST['package']) || $_REQUEST['package'] == '') {
        redirectexit('action=admin;area=packages');
    }
    $context['filename'] = $_REQUEST['package'];
    // If this is an uninstall, we'll have an id.
    $context['install_id'] = isset($_REQUEST['pid']) ? (int) $_REQUEST['pid'] : 0;
    require_once $sourcedir . '/lib/Subs-Package.php';
    // !!! TODO: Perhaps do it in steps, if necessary?
    $context['uninstalling'] = $_REQUEST['sa'] == 'uninstall2';
    // Set up the linktree for other.
    $context['linktree'][count($context['linktree']) - 1] = array('url' => $scripturl . '?action=admin;area=packages;sa=browse', 'name' => $context['uninstalling'] ? $txt['uninstall'] : $txt['extracting']);
    $context['page_title'] .= ' - ' . ($context['uninstalling'] ? $txt['uninstall'] : $txt['extracting']);
    $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?
    create_chmod_control(array(), array('destination_url' => $scripturl . '?action=admin;area=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('no_access', false);
    }
    // Are we installing this into any custom themes?
    $custom_themes = array(1);
    $known_themes = explode(',', $modSettings['knownThemes']);
    if (!empty($_POST['custom_theme'])) {
        foreach ($_POST['custom_theme'] as $tid) {
            if (in_array($tid, $known_themes)) {
                $custom_themes[] = (int) $tid;
            }
        }
    }
    // Now load up the paths of the themes that we need to know about.
    $request = smf_db_query('
		SELECT id_theme, variable, value
		FROM {db_prefix}themes
		WHERE id_theme IN ({array_int:custom_themes})
			AND variable IN ({string:name}, {string:theme_dir})', array('custom_themes' => $custom_themes, 'name' => 'name', 'theme_dir' => 'theme_dir'));
    $theme_paths = array();
    $themes_installed = array(1);
    while ($row = mysql_fetch_assoc($request)) {
        $theme_paths[$row['id_theme']][$row['variable']] = $row['value'];
    }
    mysql_free_result($request);
    // Are there any theme copying that we want to take place?
    $context['theme_copies'] = array('require-file' => array(), 'require-dir' => array());
    if (!empty($_POST['theme_changes'])) {
        foreach ($_POST['theme_changes'] as $change) {
            if (empty($change)) {
                continue;
            }
            $theme_data = unserialize(base64_decode($change));
            if (empty($theme_data['type'])) {
                continue;
            }
            $themes_installed[] = $theme_data['id'];
            $context['theme_copies'][$theme_data['type']][$theme_data['orig']][] = $theme_data['future'];
        }
    }
    // Get the package info...
    $packageInfo = getPackageInfo($context['filename']);
    if (!is_array($packageInfo)) {
        fatal_lang_error($packageInfo);
    }
    $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'], '.'));
    }
    // The mod isn't installed.... unless proven otherwise.
    $context['is_installed'] = false;
    // Is it actually installed?
    $request = smf_db_query('
		SELECT version, themes_installed, db_changes
		FROM {db_prefix}log_packages
		WHERE package_id = {string:current_package}
			AND install_state != {int:not_installed}
		ORDER BY time_installed DESC
		LIMIT 1', array('not_installed' => 0, 'current_package' => $packageInfo['id']));
    while ($row = mysql_fetch_assoc($request)) {
        $old_themes = explode(',', $row['themes_installed']);
        $old_version = $row['version'];
        $db_changes = empty($row['db_changes']) ? array() : unserialize($row['db_changes']);
    }
    mysql_free_result($request);
    // 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);
        }
        // They can only uninstall from what it was originally installed into.
        foreach ($theme_paths as $id => $data) {
            if ($id != 1 && !in_array($id, $old_themes)) {
                unset($theme_paths[$id]);
            }
        }
    } 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;
        } else {
            // Upgrade previous themes only!
            foreach ($theme_paths as $id => $data) {
                if ($id != 1 && !in_array($id, $old_themes)) {
                    unset($theme_paths[$id]);
                }
            }
        }
    } 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)) {
        $failed_steps = array();
        $failed_count = 0;
        foreach ($install_log as $action) {
            $failed_count++;
            if ($action['type'] == 'modification' && !empty($action['filename'])) {
                if ($action['boardmod']) {
                    $mod_actions = parseBoardMod(file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']), false, $action['reverse'], $theme_paths);
                } else {
                    $mod_actions = parseModification(file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']), false, $action['reverse'], $theme_paths);
                }
                // Any errors worth noting?
                foreach ($mod_actions as $key => $action) {
                    if ($action['type'] == 'failure') {
                        $failed_steps[] = array('file' => $action['filename'], 'large_step' => $failed_count, 'sub_step' => $key, 'theme' => 1);
                    }
                    // Gather the themes we installed into.
                    if (!empty($action['is_custom'])) {
                        $themes_installed[] = $action['is_custom'];
                    }
                }
            } elseif ($action['type'] == 'code' && !empty($action['filename'])) {
                // This is just here as reference for what is available.
                global $txt, $boarddir, $sourcedir, $modSettings, $context, $settings, $forum_version, $smcFunc;
                // Now include the file and be done with it ;).
                require $boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename'];
            } elseif ($action['type'] == 'database' && !empty($action['filename']) && (!$context['uninstalling'] || !empty($_POST['do_db_changes']))) {
                // These can also be there for database changes.
                global $txt, $boarddir, $sourcedir, $modSettings, $context, $settings, $forum_version, $smcFunc;
                global $db_package_log;
                // We'll likely want the package specific database functionality!
                db_extend('packages');
                // Let the file work its magic ;)
                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'] = !empty($action['filename']) && file_exists($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']) ? file_get_contents($boarddir . '/Packages/temp/' . $context['base_path'] . $action['filename']) : ($context['uninstalling'] ? $txt['package_uninstall_done'] : $txt['package_installed_done']);
                $context['redirect_timeout'] = $action['redirect_timeout'];
                // Parse out a couple of common urls.
                $urls = array('$boardurl' => $boardurl, '$scripturl' => $scripturl, '$session_var' => $context['session_var'], '$session_id' => $context['session_id']);
                $context['redirect_url'] = strtr($context['redirect_url'], $urls);
            }
        }
        package_flush_cache();
        // First, ensure this change doesn't get removed by putting a stake in the ground (So to speak).
        package_put_contents($boarddir . '/Packages/installed.list', time());
        // See if this is already installed, and change it's state as required.
        $request = smf_db_query('
			SELECT package_id, install_state, db_changes
			FROM {db_prefix}log_packages
			WHERE install_state != {int:not_installed}
				AND package_id = {string:current_package}
				' . ($context['install_id'] ? ' AND id_install = {int:install_id} ' : '') . '
			ORDER BY time_installed DESC
			LIMIT 1', array('not_installed' => 0, 'install_id' => $context['install_id'], 'current_package' => $packageInfo['id']));
        $is_upgrade = false;
        while ($row = mysql_fetch_assoc($request)) {
            // Uninstalling?
            if ($context['uninstalling']) {
                smf_db_query('
					UPDATE {db_prefix}log_packages
					SET install_state = {int:not_installed}, member_removed = {string:member_name}, id_member_removed = {int:current_member},
						time_removed = {int:current_time}
					WHERE package_id = {string:package_id}', array('current_member' => $user_info['id'], 'not_installed' => 0, 'current_time' => time(), 'package_id' => $row['package_id'], 'member_name' => $user_info['name']));
            } else {
                $is_upgrade = true;
                $old_db_changes = empty($row['db_changes']) ? array() : unserialize($row['db_changes']);
            }
        }
        // Assuming we're not uninstalling, add the entry.
        if (!$context['uninstalling']) {
            // Any db changes from older version?
            if (!empty($old_db_changes)) {
                $db_package_log = empty($db_package_log) ? $old_db_changes : array_merge($old_db_changes, $db_package_log);
            }
            // If there are some database changes we might want to remove then filter them out.
            if (!empty($db_package_log)) {
                // We're really just checking for entries which are create table AND add columns (etc).
                $tables = array();
                function sort_table_first($a, $b)
                {
                    if ($a[0] == $b[0]) {
                        return 0;
                    }
                    return $a[0] == 'remove_table' ? -1 : 1;
                }
                usort($db_package_log, 'sort_table_first');
                foreach ($db_package_log as $k => $log) {
                    if ($log[0] == 'remove_table') {
                        $tables[] = $log[1];
                    } elseif (in_array($log[1], $tables)) {
                        unset($db_package_log[$k]);
                    }
                }
                $db_changes = serialize($db_package_log);
            } else {
                $db_changes = '';
            }
            // What themes did we actually install?
            $themes_installed = array_unique($themes_installed);
            $themes_installed = implode(',', $themes_installed);
            // What failed steps?
            $failed_step_insert = serialize($failed_steps);
            smf_db_insert('', '{db_prefix}log_packages', array('filename' => 'string', 'name' => 'string', 'package_id' => 'string', 'version' => 'string', 'id_member_installed' => 'int', 'member_installed' => 'string', 'time_installed' => 'int', 'install_state' => 'int', 'failed_steps' => 'string', 'themes_installed' => 'string', 'member_removed' => 'int', 'db_changes' => 'string'), array($packageInfo['filename'], $packageInfo['name'], $packageInfo['id'], $packageInfo['version'], $user_info['id'], $user_info['name'], time(), $is_upgrade ? 2 : 1, $failed_step_insert, $themes_installed, 0, $db_changes), array('id_install'));
        }
        mysql_free_result($request);
        $context['install_finished'] = true;
    }
    // If there's database changes - and they want them removed - let's do it last!
    if (!empty($db_changes) && !empty($_POST['do_db_changes'])) {
        // We're gonna be needing the package db functions!
        db_extend('packages');
        foreach ($db_changes as $change) {
            if ($change[0] == 'remove_table' && isset($change[1])) {
                smf_db_drop_table($change[1]);
            } elseif ($change[0] == 'remove_column' && isset($change[2])) {
                smf_db_remove_column($change[1], $change[2]);
            } elseif ($change[0] == 'remove_index' && isset($change[2])) {
                smf_db_remove_index($change[1], $change[2]);
            }
        }
    }
    // Clean house... get rid of the evidence ;).
    if (file_exists($boarddir . '/Packages/temp')) {
        deltree($boarddir . '/Packages/temp');
    }
    // Log what we just did.
    logAction($context['uninstalling'] ? 'uninstall_package' : (!empty($is_upgrade) ? 'upgrade_package' : 'install_package'), array('package' => commonAPI::htmlspecialchars($packageInfo['name']), 'version' => commonAPI::htmlspecialchars($packageInfo['version'])), 'admin');
    // Just in case, let's clear the whole cache to avoid anything going up the swanny.
    clean_cache();
    // Restore file permissions?
    create_chmod_control(array(), array(), true);
}
Beispiel #5
0
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');
    }
}
Beispiel #6
0
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;
}
Beispiel #7
0
/**
 * Creates a site backup before installing a package just in case things don't go
 * as planned.
 *
 * @package Packages
 * @param string $id
 */
function package_create_backup($id = 'backup')
{
    $db = database();
    $files = array();
    // The files that reside outside of sources, in the base, we add manually
    $base_files = array('index.php', 'SSI.php', 'agreement.txt', 'ssi_examples.php', 'ssi_examples.shtml', 'subscriptions.php', 'email_imap_cron.php', 'emailpost.php', 'emailtopic.php');
    foreach ($base_files as $file) {
        if (file_exists(BOARDDIR . '/' . $file)) {
            $files[realpath(BOARDDIR . '/' . $file)] = array(empty($_REQUEST['use_full_paths']) ? $file : BOARDDIR . '/' . $file, stat(BOARDDIR . '/' . $file));
        }
    }
    // Root directory where most of our files reside
    $dirs = array(SOURCEDIR => empty($_REQUEST['use_full_paths']) ? 'sources/' : strtr(SOURCEDIR . '/', '\\', '/'));
    // Find all installed theme directories
    $request = $db->query('', '
		SELECT value
		FROM {db_prefix}themes
		WHERE id_member = {int:no_member}
			AND variable = {string:theme_dir}', array('no_member' => 0, 'theme_dir' => 'theme_dir'));
    while ($row = $db->fetch_assoc($request)) {
        $dirs[$row['value']] = empty($_REQUEST['use_full_paths']) ? 'themes/' . basename($row['value']) . '/' : strtr($row['value'] . '/', '\\', '/');
    }
    $db->free_result($request);
    // While we have directorys to check
    while (!empty($dirs)) {
        list($dir, $dest) = each($dirs);
        unset($dirs[$dir]);
        // Get the file listing for this directory
        $listing = @dir($dir);
        if (!$listing) {
            continue;
        }
        while ($entry = $listing->read()) {
            if (preg_match('~^(\\.{1,2}|CVS|backup.*|help|images|.*\\~)$~', $entry) != 0) {
                continue;
            }
            $filepath = realpath($dir . '/' . $entry);
            if (isset($files[$filepath])) {
                continue;
            }
            $stat = stat($dir . '/' . $entry);
            // If this is a directory, add it to the dir stack for processing
            if ($stat['mode'] & 040000) {
                $files[$filepath] = array($dest . $entry . '/', $stat);
                $dirs[$dir . '/' . $entry] = $dest . $entry . '/';
            } else {
                $files[$filepath] = array($dest . $entry, $stat);
            }
        }
        $listing->close();
    }
    // Make sure we have a backup directory and its writable
    if (!file_exists(BOARDDIR . '/packages/backups')) {
        mktree(BOARDDIR . '/packages/backups', 0777);
    }
    if (!is_writable(BOARDDIR . '/packages/backups')) {
        package_chmod(BOARDDIR . '/packages/backups');
    }
    // Name the output file, yyyy-mm-dd_before_package_name.tar.gz
    $output_file = BOARDDIR . '/packages/backups/' . strftime('%Y-%m-%d_') . preg_replace('~[$\\\\/:<>|?*"\']~', '', $id);
    $output_ext = '.tar' . (function_exists('gzopen') ? '.gz' : '');
    if (file_exists($output_file . $output_ext)) {
        $i = 2;
        while (file_exists($output_file . '_' . $i . $output_ext)) {
            $i++;
        }
        $output_file = $output_file . '_' . $i . $output_ext;
    } else {
        $output_file .= $output_ext;
    }
    // Buy some more time so we have enough to create this archive
    @set_time_limit(300);
    if (function_exists('apache_reset_timeout')) {
        @apache_reset_timeout();
    }
    // Set up the file output handle, try gzip first to save space
    if (function_exists('gzopen')) {
        $fwrite = 'gzwrite';
        $fclose = 'gzclose';
        $output = gzopen($output_file, 'wb');
    } else {
        $fwrite = 'fwrite';
        $fclose = 'fclose';
        $output = fopen($output_file, 'wb');
    }
    // For each file we found in the directory, we add them to a TAR archive
    foreach ($files as $real_file => $file) {
        if (!file_exists($real_file)) {
            continue;
        }
        // Check if its a directory
        $stat = $file[1];
        if (substr($file[0], -1) == '/') {
            $stat['size'] = 0;
        }
        // Create a tar file header, pack the details in to the fields
        $current = pack('a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12', $file[0], decoct($stat['mode']), sprintf('%06d', decoct($stat['uid'])), sprintf('%06d', decoct($stat['gid'])), decoct($stat['size']), decoct($stat['mtime']), '', 0, '', '', '', '', '', '', '', '', '');
        // Create the header checksum
        $checksum = 256;
        for ($i = 0; $i < 512; $i++) {
            $checksum += ord($current[$i]);
        }
        // Write out the file header (insert the checksum we just computed)
        $fwrite($output, substr($current, 0, 148) . pack('a8', decoct($checksum)) . substr($current, 156, 511));
        // If this is a directory entry all thats needed is the header
        if ($stat['size'] == 0) {
            continue;
        }
        // Write the actual file contents to the backup file
        $fp = fopen($real_file, 'rb');
        while (!feof($fp)) {
            $fwrite($output, fread($fp, 16384));
        }
        fclose($fp);
        // Pad the output so its on 512 boundarys
        $fwrite($output, pack('a' . (512 - $stat['size'] % 512), ''));
    }
    $fwrite($output, pack('a1024', ''));
    $fclose($output);
}
Beispiel #8
0
 /**
  * Does the actual writing of the file
  *
  * - Writes the extracted file to disk or if we are extracting a single file
  * - it returns the extracted data
  */
 private function _write_this_file()
 {
     $this->_skip = false;
     $this->_found = false;
     // A directory may need to be created
     if (strpos($this->_filename, '/') !== false && !$this->single_file || !$this->single_file && !is_dir($this->_file_info['dir'])) {
         mktree($this->_file_info['dir'], 0777);
     }
     // If we're looking for a **specific file**, and this is it... ka-bam, baby.
     if ($this->single_file && ($this->destination === $this->_filename || $this->destination === '*/' . basename($this->_filename))) {
         $this->_found = $this->_file_info['data'];
     } elseif ($this->single_file) {
         $this->_skip = true;
     } elseif ($this->files_to_extract !== null && !in_array($this->_filename, $this->files_to_extract)) {
         $this->_skip = true;
     }
     // Write it out then
     if ($this->_skip === true) {
         return;
     } elseif (!empty($this->_found)) {
         $this->_check_crc();
     } elseif ($this->_skip === false && $this->_found === false && $this->_check_crc()) {
         package_put_contents($this->destination . '/' . $this->_filename, $this->_file_info['data']);
     }
 }
 function mktree($ind, $indent = "", $back)
 {
     global $ctall, $deep, $type;
     $kids = array();
     foreach ($ctall as $v) {
         if ($v['parentId'] == $ind) {
             $kids[] = $v;
         }
     }
     if (count($kids)) {
         foreach ($kids as $k) {
             $back .= $indent . "|" . $k['name'] . "|tiki-browse_categories.php?parentId=" . $k['categId'] . "&amp;type=" . urlencode($type) . "&amp;deep={$deep}\n";
             $back .= mktree($k['categId'], ".{$indent}", "");
         }
         return $back;
     } else {
         return "";
     }
 }
 /**
  * Install a smiley set.
  */
 public function action_install()
 {
     global $modSettings, $scripturl, $context, $txt, $user_info;
     isAllowedTo('manage_smileys');
     checkSession('request');
     // One of these two may be necessary
     loadLanguage('Errors');
     loadLanguage('Packages');
     require_once SUBSDIR . '/Smileys.subs.php';
     require_once SUBSDIR . '/Package.subs.php';
     // Installing unless proven otherwise
     $testing = false;
     $destination = '';
     $name = '';
     if (isset($_REQUEST['set_gz'])) {
         $base_name = strtr(basename($_REQUEST['set_gz']), ':/', '-_');
         $name = Util::htmlspecialchars(strtok(basename($_REQUEST['set_gz']), '.'));
         $name_pr = preg_replace(array('/\\s/', '/\\.[\\.]+/', '/[^\\w_\\.\\-]/'), array('_', '.', ''), $name);
         $context['filename'] = $base_name;
         // Check that the smiley is from simplemachines.org, for now... maybe add mirroring later.
         if (!isAuthorizedServer($_REQUEST['set_gz']) == 0) {
             fatal_lang_error('not_valid_server');
         }
         $destination = BOARDDIR . '/packages/' . $base_name;
         if (file_exists($destination)) {
             fatal_lang_error('package_upload_error_exists');
         }
         // Let's copy it to the packages directory
         file_put_contents($destination, fetch_web_data($_REQUEST['set_gz']));
         $testing = true;
     } elseif (isset($_REQUEST['package'])) {
         $base_name = basename($_REQUEST['package']);
         $name = Util::htmlspecialchars(strtok(basename($_REQUEST['package']), '.'));
         $name_pr = preg_replace(array('/\\s/', '/\\.[\\.]+/', '/[^\\w_\\.\\-]/'), array('_', '.', ''), $name);
         $context['filename'] = $base_name;
         $destination = BOARDDIR . '/packages/' . basename($_REQUEST['package']);
     }
     if (!file_exists($destination)) {
         fatal_lang_error('package_no_file', false);
     }
     // Make sure temp directory exists and is empty.
     if (file_exists(BOARDDIR . '/packages/temp')) {
         deltree(BOARDDIR . '/packages/temp', false);
     }
     if (!mktree(BOARDDIR . '/packages/temp', 0755)) {
         deltree(BOARDDIR . '/packages/temp', false);
         if (!mktree(BOARDDIR . '/packages/temp', 0777)) {
             deltree(BOARDDIR . '/packages/temp', false);
             // @todo not sure about url in destination_url
             create_chmod_control(array(BOARDDIR . '/packages/temp/delme.tmp'), array('destination_url' => $scripturl . '?action=admin;area=smileys;sa=install;set_gz=' . $_REQUEST['set_gz'], 'crash_on_error' => true));
             deltree(BOARDDIR . '/packages/temp', false);
             if (!mktree(BOARDDIR . '/packages/temp', 0777)) {
                 fatal_lang_error('package_cant_download', false);
             }
         }
     }
     $extracted = read_tgz_file($destination, BOARDDIR . '/packages/temp');
     // @todo needs to change the URL in the next line ;)
     if (!$extracted) {
         fatal_lang_error('packageget_unable', false, array('http://custom.elkarte.net/index.php?action=search;type=12;basic_search=' . $name));
     }
     if ($extracted && !file_exists(BOARDDIR . '/packages/temp/package-info.xml')) {
         foreach ($extracted as $file) {
             if (basename($file['filename']) == 'package-info.xml') {
                 $base_path = dirname($file['filename']) . '/';
                 break;
             }
         }
     }
     if (!isset($base_path)) {
         $base_path = '';
     }
     if (!file_exists(BOARDDIR . '/packages/temp/' . $base_path . 'package-info.xml')) {
         fatal_lang_error('package_get_error_missing_xml', false);
     }
     $smileyInfo = getPackageInfo($context['filename']);
     if (!is_array($smileyInfo)) {
         fatal_lang_error($smileyInfo);
     }
     // See if it is installed?
     if (isSmileySetInstalled($smileyInfo['id'])) {
         fata_lang_error('package_installed_warning1');
     }
     // Everything is fine, now it's time to do something, first we test
     $actions = parsePackageInfo($smileyInfo['xml'], true, 'install');
     $context['post_url'] = $scripturl . '?action=admin;area=smileys;sa=install;package=' . $base_name;
     $context['has_failure'] = false;
     $context['actions'] = array();
     $context['ftp_needed'] = false;
     foreach ($actions as $action) {
         if ($action['type'] == 'readme' || $action['type'] == 'license') {
             $type = 'package_' . $action['type'];
             if (file_exists(BOARDDIR . '/packages/temp/' . $base_path . $action['filename'])) {
                 $context[$type] = htmlspecialchars(trim(file_get_contents(BOARDDIR . '/packages/temp/' . $base_path . $action['filename']), "\n\r"), ENT_COMPAT, 'UTF-8');
             } elseif (file_exists($action['filename'])) {
                 $context[$type] = htmlspecialchars(trim(file_get_contents($action['filename']), "\n\r"), ENT_COMPAT, 'UTF-8');
             }
             if (!empty($action['parse_bbc'])) {
                 require_once SUBSDIR . '/Post.subs.php';
                 preparsecode($context[$type]);
                 $context[$type] = parse_bbc($context[$type]);
             } else {
                 $context[$type] = nl2br($context[$type]);
             }
             continue;
         } elseif ($action['type'] == 'require-dir') {
             // Do this one...
             $thisAction = array('type' => $txt['package_extract'] . ' ' . ($action['type'] == 'require-dir' ? $txt['package_tree'] : $txt['package_file']), 'action' => Util::htmlspecialchars(strtr($action['destination'], array(BOARDDIR => '.'))));
             $file = BOARDDIR . '/packages/temp/' . $base_path . $action['filename'];
             if (isset($action['filename']) && (!file_exists($file) || !is_writable(dirname($action['destination'])))) {
                 $context['has_failure'] = true;
                 $thisAction += array('description' => $txt['package_action_error'], 'failed' => true);
             }
             // Show a description for the action if one is provided
             if (empty($thisAction['description'])) {
                 $thisAction['description'] = isset($action['description']) ? $action['description'] : '';
             }
             $context['actions'][] = $thisAction;
         } elseif ($action['type'] == 'credits') {
             // Time to build the billboard
             $credits_tag = array('url' => $action['url'], 'license' => $action['license'], 'copyright' => $action['copyright'], 'title' => $action['title']);
         }
     }
     if ($testing) {
         $context['sub_template'] = 'view_package';
         $context['uninstalling'] = false;
         $context['is_installed'] = false;
         $context['package_name'] = $smileyInfo['name'];
         loadTemplate('Packages');
     } else {
         $actions = parsePackageInfo($smileyInfo['xml'], false, 'install');
         foreach ($context['actions'] as $action) {
             updateSettings(array('smiley_sets_known' => $modSettings['smiley_sets_known'] . ',' . basename($action['action']), 'smiley_sets_names' => $modSettings['smiley_sets_names'] . "\n" . $smileyInfo['name'] . (count($context['actions']) > 1 ? ' ' . (!empty($action['description']) ? Util::htmlspecialchars($action['description']) : basename($action['action'])) : '')));
         }
         package_flush_cache();
         // Time to tell pacman we have a new package installed!
         package_put_contents(BOARDDIR . '/packages/installed.list', time());
         // Credits tag?
         $credits_tag = empty($credits_tag) ? '' : serialize($credits_tag);
         $installed = array('filename' => $smileyInfo['filename'], 'name' => $smileyInfo['name'], 'package_id' => $smileyInfo['id'], 'version' => $smileyInfo['filename'], 'id_member' => $user_info['id'], 'member_name' => $user_info['name'], 'credits_tag' => $credits_tag);
         logPackageInstall($installed);
         logAction('install_package', array('package' => Util::htmlspecialchars($smileyInfo['name']), 'version' => Util::htmlspecialchars($smileyInfo['version'])), 'admin');
         cache_put_data('parsing_smileys', null, 480);
         cache_put_data('posting_smileys', null, 480);
     }
     if (file_exists(BOARDDIR . '/packages/temp')) {
         deltree(BOARDDIR . '/packages/temp');
     }
     if (!$testing) {
         redirectexit('action=admin;area=smileys');
     }
 }
Beispiel #11
0
 /**
  * Does the actual writing of the file
  *
  * - Writes the extracted file to disk or if we are extracting a single file
  * - it returns the extracted data
  */
 private function _write_this_file()
 {
     $this->_skip = false;
     $this->_found = false;
     // A directory may need to be created
     if (strpos($this->_current['filename'], '/') !== false && !$this->single_file) {
         mktree($this->destination . '/' . dirname($this->_current['filename']), 0777);
     }
     // Is this the file we're looking for?
     if ($this->single_file && ($this->destination === $this->_current['filename'] || $this->destination === '*/' . basename($this->_current['filename']))) {
         $this->_found = $this->_current['data'];
     } elseif ($this->single_file) {
         $this->_skip = true;
     } elseif ($this->files_to_extract !== null && !in_array($this->_current['filename'], $this->files_to_extract)) {
         $this->_skip = true;
     }
     // Write it out then
     if ($this->_check_header_crc() && $this->_skip === false && $this->_found === false) {
         package_put_contents($this->destination . '/' . $this->_current['filename'], $this->_current['data']);
     }
 }