Exemplo n.º 1
0
function list_getLanguagesList()
{
    global $forum_version, $context, $sourcedir, $smcFunc, $txt, $scripturl;
    // We're going to use this URL.
    $url = 'http://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => '')));
    // Load the class file and stick it into an array.
    require_once $sourcedir . '/Class-Package.php';
    $language_list = new xmlArray(fetch_web_data($url), true);
    // Check that the site responded and that the language exists.
    if (!$language_list->exists('languages')) {
        $context['smf_error'] = 'no_response';
    } elseif (!$language_list->exists('languages/language')) {
        $context['smf_error'] = 'no_files';
    } else {
        $language_list = $language_list->path('languages[0]');
        $lang_files = $language_list->set('language');
        $smf_languages = array();
        foreach ($lang_files as $file) {
            // Were we searching?
            if (!empty($context['smf_search_term']) && strpos($file->fetch('name'), $smcFunc['strtolower']($context['smf_search_term'])) === false) {
                continue;
            }
            $smf_languages[] = array('id' => $file->fetch('id'), 'name' => $smcFunc['ucwords']($file->fetch('name')), 'version' => $file->fetch('version'), 'utf8' => $file->fetch('utf8') ? $txt['yes'] : $txt['no'], 'description' => $file->fetch('description'), 'install_link' => '<a href="' . $scripturl . '?action=admin;area=languages;sa=downloadlang;did=' . $file->fetch('id') . ';' . $context['session_var'] . '=' . $context['session_id'] . '">' . $txt['add_language_smf_install'] . '</a>');
        }
        if (empty($smf_languages)) {
            $context['smf_error'] = 'no_files';
        } else {
            return $smf_languages;
        }
    }
}
Exemplo n.º 2
0
function PackageGBrowse()
{
    global $txt, $boardurl, $context, $scripturl, $boarddir, $sourcedir, $forum_version, $context, $db_prefix;
    if (isset($_GET['server'])) {
        if ($_GET['server'] == '') {
            redirectexit('action=packageget');
        }
        $server = (int) $_GET['server'];
        // Query the server list to find the current server.
        $request = db_query("\n\t\t\tSELECT name, url\n\t\t\tFROM {$db_prefix}package_servers\n\t\t\tWHERE ID_SERVER = {$server}\n\t\t\tLIMIT 1", __FILE__, __LINE__);
        list($name, $url) = mysql_fetch_row($request);
        mysql_free_result($request);
        // If the server does not exist, dump out.
        if (empty($url)) {
            fatal_lang_error('smf191', false);
        }
        // If there is a relative link, append to the stored server url.
        if (isset($_GET['relative'])) {
            $url = $url . (substr($url, -1) == '/' ? '' : '/') . $_GET['relative'];
        }
        // Clear any "absolute" URL.  Since "server" is present, "absolute" is garbage.
        unset($_GET['absolute']);
    } elseif (isset($_GET['absolute']) && $_GET['absolute'] != '') {
        // Initialize the requried variables.
        $server = '';
        $url = $_GET['absolute'];
        $name = '';
        $_GET['package'] = $url . '/packages.xml?language=' . $context['user']['language'];
        // Clear any "relative" URL.  Since "server" is not present, "relative" is garbage.
        unset($_GET['relative']);
        $token = checkConfirm('get_absolute_url');
        if ($token !== true) {
            $context['sub_template'] = 'package_confirm';
            $context['page_title'] = $txt['smf183'];
            $context['confirm_message'] = sprintf($txt['package_confirm_view_package_content'], htmlspecialchars($_GET['absolute']));
            $context['proceed_href'] = $scripturl . '?action=packageget;sa=browse;absolute=' . urlencode($_GET['absolute']) . ';confirm=' . $token;
            return;
        }
    } else {
        fatal_lang_error('smf191', false);
    }
    // In safe mode or on lycos?  Try this URL. (includes package-list for informational purposes ;).)
    //if (@ini_get('safe_mode'))
    //	redirectexit($url . '/index.php?package-list&language=' . $context['user']['language'] . '&ref=' . $boardurl);
    // Attempt to connect.  If unsuccessful... try the URL.
    if (!isset($_GET['package']) || file_exists($_GET['package'])) {
        $_GET['package'] = $url . '/packages.xml?language=' . $context['user']['language'];
    }
    // Check to be sure the packages.xml file actually exists where it is should be... or dump out.
    if ((isset($_GET['absolute']) || isset($_GET['relative'])) && !url_exists($_GET['package'])) {
        fatal_lang_error('packageget_unable', false, array($url . '/index.php'));
    }
    // Read packages.xml and parse into xmlArray. (the true tells it to trim things ;).)
    $listing = new xmlArray(fetch_web_data($_GET['package']), true);
    // Errm.... empty file?  Try the URL....
    if (!$listing->exists('package-list')) {
        fatal_lang_error('packageget_unable', false, array($url . '/index.php'));
    }
    // List out the packages...
    $context['package_list'] = array();
    $listing = $listing->path('package-list[0]');
    // Use the package list's name if it exists.
    if ($listing->exists('list-title')) {
        $name = $listing->fetch('list-title');
    }
    // Pick the correct template.
    $context['sub_template'] = 'package_list';
    $context['page_title'] = $txt['smf183'] . ($name != '' ? ' - ' . $name : '');
    $context['package_server'] = $server;
    $instmods = loadInstalledPackages();
    // Look through the list of installed mods...
    foreach ($instmods as $installed_mod) {
        $installed_mods[$installed_mod['id']] = $installed_mod['version'];
    }
    // Get default author and email if they exist.
    if ($listing->exists('default-author')) {
        $default_author = htmlspecialchars($listing->fetch('default-author'));
        if ($listing->exists('default-author/@email')) {
            $default_email = $listing->fetch('default-author/@email');
        }
    }
    // Get default web site if it exists.
    if ($listing->exists('default-website')) {
        $default_website = $listing->fetch('default-website');
        if ($listing->exists('default-website/@title')) {
            $default_title = htmlspecialchars($listing->fetch('default-website/@title'));
        }
    }
    $the_version = strtr($forum_version, array('SMF ' => ''));
    if (!empty($_SESSION['version_emulate'])) {
        $the_version = $_SESSION['version_emulate'];
    }
    $packageNum = 0;
    $sections = $listing->set('section');
    foreach ($sections as $i => $section) {
        $packages = $section->set('title|heading|text|remote|rule|modification|language|avatar-pack|theme|smiley-set');
        foreach ($packages as $thisPackage) {
            $package =& $context['package_list'][];
            $package['type'] = $thisPackage->name();
            // It's a Title, Heading, Rule or Text.
            if (in_array($package['type'], array('title', 'heading', 'text', 'rule'))) {
                $package['name'] = htmlspecialchars($thisPackage->fetch('.'));
            } elseif ($package['type'] == 'remote') {
                $remote_type = $thisPackage->exists('@type') ? $thisPackage->fetch('@type') : 'relative';
                if ($remote_type == 'relative' && substr($thisPackage->fetch('@href'), 0, 7) != 'http://') {
                    if (isset($_GET['absolute'])) {
                        $current_url = $_GET['absolute'] . '/';
                    } elseif (isset($_GET['relative'])) {
                        $current_url = $_GET['relative'] . '/';
                    } else {
                        $current_url = '';
                    }
                    $current_url .= $thisPackage->fetch('@href');
                    if (isset($_GET['absolute'])) {
                        $package['href'] = $scripturl . '?action=packageget;sa=browse;absolute=' . $current_url;
                    } else {
                        $package['href'] = $scripturl . '?action=packageget;sa=browse;server=' . $context['package_server'] . ';relative=' . $current_url;
                    }
                } else {
                    $current_url = $thisPackage->fetch('@href');
                    $package['href'] = $scripturl . '?action=packageget;sa=browse;absolute=' . $current_url;
                }
                $package['name'] = htmlspecialchars($thisPackage->fetch('.'));
                $package['link'] = '<a href="' . $package['href'] . '">' . $package['name'] . '</a>';
            } else {
                if (isset($_GET['absolute'])) {
                    $current_url = $_GET['absolute'] . '/';
                } elseif (isset($_GET['relative'])) {
                    $current_url = $_GET['relative'] . '/';
                } else {
                    $current_url = '';
                }
                $server_att = $server != '' ? ';server=' . $server : '';
                $package += $thisPackage->to_array();
                if (isset($package['website'])) {
                    unset($package['website']);
                }
                $package['author'] = array();
                if ($package['description'] == '') {
                    $package['description'] = $txt['pacman8'];
                } else {
                    $package['description'] = parse_bbc(preg_replace('~\\[[/]?html\\]~i', '', htmlspecialchars($package['description'])));
                }
                $package['is_installed'] = isset($installed_mods[$package['id']]);
                $package['is_current'] = $package['is_installed'] && $installed_mods[$package['id']] == $package['version'];
                $package['is_newer'] = $package['is_installed'] && $installed_mods[$package['id']] > $package['version'];
                // This package is either not installed, or installed but old.  Is it supported on this version of SMF?
                if (!$package['is_installed'] || !$package['is_current'] && !$package['is_newer']) {
                    if ($thisPackage->exists('version/@for')) {
                        $package['can_install'] = matchPackageVersion($the_version, $thisPackage->fetch('version/@for'));
                    }
                } else {
                    $package['can_install'] = false;
                }
                $already_exists = getPackageInfo(basename($package['filename']));
                $package['download_conflict'] = !empty($already_exists) && $already_exists['id'] == $package['id'] && $already_exists['version'] != $package['version'];
                $package['href'] = $url . '/' . $package['filename'];
                $package['name'] = htmlspecialchars($package['name']);
                $package['link'] = '<a href="' . $package['href'] . '">' . $package['name'] . '</a>';
                $package['download']['href'] = $scripturl . '?action=packageget;sa=download' . $server_att . ';package=' . $current_url . $package['filename'] . ($package['download_conflict'] ? ';conflict' : '') . ';sesc=' . $context['session_id'];
                $package['download']['link'] = '<a href="' . $package['download']['href'] . '">' . $package['name'] . '</a>';
                if ($thisPackage->exists('author') || isset($default_author)) {
                    if ($thisPackage->exists('author/@email')) {
                        $package['author']['email'] = htmlspecialchars($thisPackage->fetch('author/@email'));
                    } elseif (isset($default_email)) {
                        $package['author']['email'] = $default_email;
                    }
                    if ($thisPackage->exists('author') && $thisPackage->fetch('author') != '') {
                        $package['author']['name'] = htmlspecialchars($thisPackage->fetch('author'));
                    } else {
                        $package['author']['name'] = $default_author;
                    }
                    if (!empty($package['author']['email'])) {
                        // Only put the "mailto:" if it looks like a valid email address.  Some may wish to put a link to an SMF IM Form or other web mail form.
                        $package['author']['href'] = preg_match('~^[\\w\\.\\-]+@[\\w][\\w\\-\\.]+[\\w]$~', $package['author']['email']) != 0 ? 'mailto:' . $package['author']['email'] : $package['author']['email'];
                        $package['author']['link'] = '<a href="' . $package['author']['href'] . '">' . $package['author']['name'] . '</a>';
                    }
                }
                if ($thisPackage->exists('website') || isset($default_website)) {
                    if ($thisPackage->exists('website') && $thisPackage->exists('website/@title')) {
                        $package['author']['website']['name'] = htmlspecialchars($thisPackage->fetch('website/@title'));
                    } elseif (isset($default_title)) {
                        $package['author']['website']['name'] = $default_title;
                    } elseif ($thisPackage->exists('website')) {
                        $package['author']['website']['name'] = htmlspecialchars($thisPackage->fetch('website'));
                    } else {
                        $package['author']['website']['name'] = $default_website;
                    }
                    if ($thisPackage->exists('website') && $thisPackage->fetch('website') != '') {
                        $authorhompage = $thisPackage->fetch('website');
                    } else {
                        $authorhompage = $default_website;
                    }
                    if (strpos(strtolower($authorhompage), 'a href') === false) {
                        $package['author']['website']['href'] = $authorhompage;
                        $package['author']['website']['link'] = '<a href="' . $authorhompage . '">' . $package['author']['website']['name'] . '</a>';
                    } else {
                        if (preg_match('/a href="(.+?)"/', $authorhompage, $match) == 1) {
                            $package['author']['website']['href'] = $match[1];
                        } else {
                            $package['author']['website']['href'] = '';
                        }
                        $package['author']['website']['link'] = $authorhompage;
                    }
                } else {
                    $package['author']['website']['href'] = '';
                    $package['author']['website']['link'] = '';
                }
            }
            $package['is_remote'] = $package['type'] == 'remote';
            $package['is_title'] = $package['type'] == 'title';
            $package['is_heading'] = $package['type'] == 'heading';
            $package['is_text'] = $package['type'] == 'text';
            $package['is_line'] = $package['type'] == 'rule';
            $packageNum = in_array($package['type'], array('title', 'heading', 'text', 'remote', 'rule')) ? 0 : $packageNum + 1;
            $package['count'] = $packageNum;
        }
    }
    // Lets make sure we get a nice new spiffy clean $package to work with.  Otherwise we get PAIN!
    unset($package);
    foreach ($context['package_list'] as $i => $package) {
        if ($package['count'] == 0 || isset($package['can_install'])) {
            continue;
        }
        $context['package_list'][$i]['can_install'] = false;
        $packageInfo = getPackageInfo($url . '/' . $package['filename']);
        if (!empty($packageInfo) && $packageInfo['xml']->exists('install')) {
            $installs = $packageInfo['xml']->set('install');
            foreach ($installs as $install) {
                if (!$install->exists('@for') || matchPackageVersion($the_version, $install->fetch('@for'))) {
                    // Okay, this one is good to go.
                    $context['package_list'][$i]['can_install'] = true;
                    break;
                }
            }
        }
    }
}
Exemplo n.º 3
0
function parseModification($file, $testing = true, $undo = false, $theme_paths = array())
{
    global $boarddir, $sourcedir, $settings, $txt, $modSettings, $package_ftp;
    @set_time_limit(600);
    loadClassFile('Class-Package.php');
    $xml = new xmlArray(strtr($file, array("\r" => '')));
    $actions = array();
    $everything_found = true;
    if (!$xml->exists('modification') || !$xml->exists('modification/file')) {
        $actions[] = array('type' => 'error', 'filename' => '-', 'debug' => $txt['package_modification_malformed']);
        return $actions;
    }
    // Get the XML data.
    $files = $xml->set('modification/file');
    // Use this for holding all the template changes in this mod.
    $template_changes = array();
    // This is needed to hold the long paths, as they can vary...
    $long_changes = array();
    // First, we need to build the list of all the files likely to get changed.
    foreach ($files as $file) {
        // What is the filename we're currently on?
        $filename = parse_path(trim($file->fetch('@name')));
        // Now, we need to work out whether this is even a template file...
        foreach ($theme_paths as $id => $theme) {
            // If this filename is relative, if so take a guess at what it should be.
            $real_filename = $filename;
            if (strpos($filename, 'Themes') === 0) {
                $real_filename = $boarddir . '/' . $filename;
            }
            if (strpos($real_filename, $theme['theme_dir']) === 0) {
                $template_changes[$id][] = substr($real_filename, strlen($theme['theme_dir']) + 1);
                $long_changes[$id][] = $filename;
            }
        }
    }
    // Custom themes to add.
    $custom_themes_add = array();
    // If we have some template changes, we need to build a master link of what new ones are required for the custom themes.
    if (!empty($template_changes[1])) {
        foreach ($theme_paths as $id => $theme) {
            // Default is getting done anyway, so no need for involvement here.
            if ($id == 1) {
                continue;
            }
            // For every template, do we want it? Yea, no, maybe?
            foreach ($template_changes[1] as $index => $template_file) {
                // What, it exists and we haven't already got it?! Lordy, get it in!
                if (file_exists($theme['theme_dir'] . '/' . $template_file) && (!isset($template_changes[$id]) || !in_array($template_file, $template_changes[$id]))) {
                    // Now let's add it to the "todo" list.
                    $custom_themes_add[$long_changes[1][$index]][$id] = $theme['theme_dir'] . '/' . $template_file;
                }
            }
        }
    }
    foreach ($files as $file) {
        // This is the actual file referred to in the XML document...
        $files_to_change = array(1 => parse_path(trim($file->fetch('@name'))));
        // Sometimes though, we have some additional files for other themes, if we have add them to the mix.
        if (isset($custom_themes_add[$files_to_change[1]])) {
            $files_to_change += $custom_themes_add[$files_to_change[1]];
        }
        // Now, loop through all the files we're changing, and, well, change them ;)
        foreach ($files_to_change as $theme => $working_file) {
            if ($working_file[0] != '/' && $working_file[1] != ':') {
                trigger_error('parseModification(): The filename \'' . $working_file . '\' is not a full path!', E_USER_WARNING);
                $working_file = $boarddir . '/' . $working_file;
            }
            // Doesn't exist - give an error or what?
            if (!file_exists($working_file) && (!$file->exists('@error') || !in_array(trim($file->fetch('@error')), array('ignore', 'skip')))) {
                $actions[] = array('type' => 'missing', 'filename' => $working_file, 'debug' => $txt['package_modification_missing']);
                $everything_found = false;
                continue;
            } elseif (!file_exists($working_file) && $file->exists('@error') && trim($file->fetch('@error')) == 'skip') {
                $actions[] = array('type' => 'skipping', 'filename' => $working_file);
                continue;
            } elseif (!file_exists($working_file)) {
                $working_data = '';
            } else {
                $working_data = str_replace("\r", '', package_get_contents($working_file));
            }
            $actions[] = array('type' => 'opened', 'filename' => $working_file);
            $operations = $file->exists('operation') ? $file->set('operation') : array();
            foreach ($operations as $operation) {
                // Convert operation to an array.
                $actual_operation = array('searches' => array(), 'error' => $operation->exists('@error') && in_array(trim($operation->fetch('@error')), array('ignore', 'fatal', 'required')) ? trim($operation->fetch('@error')) : 'fatal');
                // The 'add' parameter is used for all searches in this operation.
                $add = $operation->exists('add') ? $operation->fetch('add') : '';
                // Grab all search items of this operation (in most cases just 1).
                $searches = $operation->set('search');
                foreach ($searches as $i => $search) {
                    $actual_operation['searches'][] = array('position' => $search->exists('@position') && in_array(trim($search->fetch('@position')), array('before', 'after', 'replace', 'end')) ? trim($search->fetch('@position')) : 'replace', 'is_reg_exp' => $search->exists('@regexp') && trim($search->fetch('@regexp')) === 'true', 'loose_whitespace' => $search->exists('@whitespace') && trim($search->fetch('@whitespace')) === 'loose', 'search' => $search->fetch('.'), 'add' => $add, 'preg_search' => '', 'preg_replace' => '');
                }
                // At least one search should be defined.
                if (empty($actual_operation['searches'])) {
                    $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $search['search'], 'is_custom' => $theme > 1 ? $theme : 0);
                    // Skip to the next operation.
                    continue;
                }
                // Reverse the operations in case of undoing stuff.
                if ($undo) {
                    foreach ($actual_operation['searches'] as $i => $search) {
                        // Reverse modification of regular expressions are not allowed.
                        if ($search['is_reg_exp']) {
                            if ($actual_operation['error'] === 'fatal') {
                                $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $search['search'], 'is_custom' => $theme > 1 ? $theme : 0);
                            }
                            // Continue to the next operation.
                            continue 2;
                        }
                        // The replacement is now the search subject...
                        if ($search['position'] === 'replace' || $search['position'] === 'end') {
                            $actual_operation['searches'][$i]['search'] = $search['add'];
                        } else {
                            // Reversing a before/after modification becomes a replacement.
                            $actual_operation['searches'][$i]['position'] = 'replace';
                            if ($search['position'] === 'before') {
                                $actual_operation['searches'][$i]['search'] .= $search['add'];
                            } elseif ($search['position'] === 'after') {
                                $actual_operation['searches'][$i]['search'] = $search['add'] . $search['search'];
                            }
                        }
                        // ...and the search subject is now the replacement.
                        $actual_operation['searches'][$i]['add'] = $search['search'];
                    }
                }
                // Sort the search list so the replaces come before the add before/after's.
                if (count($actual_operation['searches']) !== 1) {
                    $replacements = array();
                    foreach ($actual_operation['searches'] as $i => $search) {
                        if ($search['position'] === 'replace') {
                            $replacements[] = $search;
                            unset($actual_operation['searches'][$i]);
                        }
                    }
                    $actual_operation['searches'] = array_merge($replacements, $actual_operation['searches']);
                }
                // Create regular expression replacements from each search.
                foreach ($actual_operation['searches'] as $i => $search) {
                    // Not much needed if the search subject is already a regexp.
                    if ($search['is_reg_exp']) {
                        $actual_operation['searches'][$i]['preg_search'] = $search['search'];
                    } else {
                        // Make the search subject fit into a regular expression.
                        $actual_operation['searches'][$i]['preg_search'] = preg_quote($search['search'], '~');
                        // Using 'loose', a random amount of tabs and spaces may be used.
                        if ($search['loose_whitespace']) {
                            $actual_operation['searches'][$i]['preg_search'] = preg_replace('~[ \\t]+~', '[ \\t]+', $actual_operation['searches'][$i]['preg_search']);
                        }
                    }
                    // Shuzzup.  This is done so we can safely use a regular expression. ($0 is bad!!)
                    $actual_operation['searches'][$i]['preg_replace'] = strtr($search['add'], array('$' => '[$PACK' . 'AGE1$]', '\\' => '[$PACK' . 'AGE2$]'));
                    // Before, so the replacement comes after the search subject :P
                    if ($search['position'] === 'before') {
                        $actual_operation['searches'][$i]['preg_search'] = '(' . $actual_operation['searches'][$i]['preg_search'] . ')';
                        $actual_operation['searches'][$i]['preg_replace'] = '$1' . $actual_operation['searches'][$i]['preg_replace'];
                    } elseif ($search['position'] === 'after') {
                        $actual_operation['searches'][$i]['preg_search'] = '(' . $actual_operation['searches'][$i]['preg_search'] . ')';
                        $actual_operation['searches'][$i]['preg_replace'] .= '$1';
                    } elseif ($search['position'] === 'end') {
                        if ($undo) {
                            $actual_operation['searches'][$i]['preg_replace'] = '';
                        } else {
                            $actual_operation['searches'][$i]['preg_search'] = '(\\n\\?\\>)?$';
                            $actual_operation['searches'][$i]['preg_replace'] .= '$1';
                        }
                    }
                    // Testing 1, 2, 3...
                    $failed = preg_match('~' . $actual_operation['searches'][$i]['preg_search'] . '~s', $working_data) === 0;
                    // Nope, search pattern not found.
                    if ($failed && $actual_operation['error'] === 'fatal') {
                        $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $actual_operation['searches'][$i]['preg_search'], 'search_original' => $actual_operation['searches'][$i]['search'], 'replace_original' => $actual_operation['searches'][$i]['add'], 'position' => $search['position'], 'is_custom' => $theme > 1 ? $theme : 0, 'failed' => $failed);
                        $everything_found = false;
                        continue;
                    } elseif (!$failed && $actual_operation['error'] === 'required') {
                        $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $actual_operation['searches'][$i]['preg_search'], 'search_original' => $actual_operation['searches'][$i]['search'], 'replace_original' => $actual_operation['searches'][$i]['add'], 'position' => $search['position'], 'is_custom' => $theme > 1 ? $theme : 0, 'failed' => $failed);
                        $everything_found = false;
                        continue;
                    }
                    // Replace it into nothing? That's not an option...unless it's an undoing end.
                    if ($search['add'] === '' && ($search['position'] !== 'end' || !$undo)) {
                        continue;
                    }
                    // Finally, we're doing some replacements.
                    $working_data = preg_replace('~' . $actual_operation['searches'][$i]['preg_search'] . '~s', $actual_operation['searches'][$i]['preg_replace'], $working_data, 1);
                    $actions[] = array('type' => 'replace', 'filename' => $working_file, 'search' => $actual_operation['searches'][$i]['preg_search'], 'replace' => $actual_operation['searches'][$i]['preg_replace'], 'search_original' => $actual_operation['searches'][$i]['search'], 'replace_original' => $actual_operation['searches'][$i]['add'], 'position' => $search['position'], 'failed' => $failed, 'ignore_failure' => $failed && $actual_operation['error'] === 'ignore', 'is_custom' => $theme > 1 ? $theme : 0);
                }
            }
            // Fix any little helper symbols ;).
            $working_data = strtr($working_data, array('[$PACK' . 'AGE1$]' => '$', '[$PACK' . 'AGE2$]' => '\\'));
            package_chmod($working_file);
            if (file_exists($working_file) && !is_writable($working_file) || !file_exists($working_file) && !is_writable(dirname($working_file))) {
                $actions[] = array('type' => 'chmod', 'filename' => $working_file);
            }
            if (basename($working_file) == 'Settings_bak.php') {
                continue;
            }
            if (!$testing && !empty($modSettings['package_make_backups']) && file_exists($working_file)) {
                // No, no, not Settings.php!
                if (basename($working_file) == 'Settings.php') {
                    @copy($working_file, dirname($working_file) . '/Settings_bak.php');
                } else {
                    @copy($working_file, $working_file . '~');
                }
            }
            // Always call this, even if in testing, because it won't really be written in testing mode.
            package_put_contents($working_file, $working_data, $testing);
            $actions[] = array('type' => 'saved', 'filename' => $working_file, 'is_custom' => $theme > 1 ? $theme : 0);
        }
    }
    $actions[] = array('type' => 'result', 'status' => $everything_found);
    return $actions;
}
Exemplo n.º 4
0
function AddLanguage()
{
    global $context, $sourcedir, $forum_version, $boarddir, $txt, $smcFunc, $scripturl;
    // Are we searching for new languages courtesy of Simple Machines?
    if (!empty($_POST['smf_add_sub'])) {
        // Need fetch_web_data.
        require_once $sourcedir . '/Subs-Package.php';
        $context['smf_search_term'] = htmlspecialchars(trim($_POST['smf_add']));
        // We're going to use this URL.
        $url = 'http://download.simplemachines.org/fetch_language.php?version=' . urlencode(strtr($forum_version, array('SMF ' => '')));
        // Load the class file and stick it into an array.
        loadClassFile('Class-Package.php');
        $language_list = new xmlArray(fetch_web_data($url), true);
        // Check it exists.
        if (!$language_list->exists('languages')) {
            $context['smf_error'] = 'no_response';
        } else {
            $language_list = $language_list->path('languages[0]');
            $lang_files = $language_list->set('language');
            $context['smf_languages'] = array();
            foreach ($lang_files as $file) {
                // Were we searching?
                if (!empty($context['smf_search_term']) && strpos($file->fetch('name'), $smcFunc['strtolower']($context['smf_search_term'])) === false) {
                    continue;
                }
                $context['smf_languages'][] = array('id' => $file->fetch('id'), 'name' => $smcFunc['ucwords']($file->fetch('name')), 'version' => $file->fetch('version'), 'utf8' => $file->fetch('utf8'), 'description' => $file->fetch('description'), 'link' => $scripturl . '?action=admin;area=languages;sa=downloadlang;did=' . $file->fetch('id') . ';' . $context['session_var'] . '=' . $context['session_id']);
            }
            if (empty($context['smf_languages'])) {
                $context['smf_error'] = 'no_files';
            }
        }
    }
    $context['sub_template'] = 'add_language';
}
Exemplo n.º 5
0
/**
 * This file allows the user to search the SM online manual for a little of help.
 */
function AdminSearchOM()
{
    global $context, $sourcedir;
    $context['doc_apiurl'] = 'http://wiki.simplemachines.org/api.php';
    $context['doc_scripturl'] = 'http://wiki.simplemachines.org/smf/';
    // Set all the parameters search might expect.
    $postVars = explode(' ', $context['search_term']);
    // Encode the search data.
    foreach ($postVars as $k => $v) {
        $postVars[$k] = urlencode($v);
    }
    // This is what we will send.
    $postVars = implode('+', $postVars);
    // Get the results from the doc site.
    require_once $sourcedir . '/Subs-Package.php';
    // Demo URL:
    // http://wiki.simplemachines.org/api.php?action=query&list=search&srprop=timestamp|snippet&format=xml&srwhat=text&srsearch=template+eval
    $search_results = fetch_web_data($context['doc_apiurl'] . '?action=query&list=search&srprop=timestamp|snippet&format=xml&srwhat=text&srsearch=' . $postVars);
    // If we didn't get any xml back we are in trouble - perhaps the doc site is overloaded?
    if (!$search_results || preg_match('~<' . '\\?xml\\sversion="\\d+\\.\\d+"\\?>\\s*(<api>.+?</api>)~is', $search_results, $matches) != true) {
        fatal_lang_error('cannot_connect_doc_site');
    }
    $search_results = $matches[1];
    // Otherwise we simply walk through the XML and stick it in context for display.
    $context['search_results'] = array();
    require_once $sourcedir . '/Class-Package.php';
    // Get the results loaded into an array for processing!
    $results = new xmlArray($search_results, false);
    // Move through the api layer.
    if (!$results->exists('api')) {
        fatal_lang_error('cannot_connect_doc_site');
    }
    // Are there actually some results?
    if ($results->exists('api/query/search/p')) {
        $relevance = 0;
        foreach ($results->set('api/query/search/p') as $result) {
            $context['search_results'][$result->fetch('@title')] = array('title' => $result->fetch('@title'), 'relevance' => $relevance++, 'snippet' => str_replace('class=\'searchmatch\'', 'class="highlight"', un_htmlspecialchars($result->fetch('@snippet'))));
        }
    }
}
Exemplo n.º 6
0
function AdminSearchOM()
{
    global $context, $sourcedir;
    $docsURL = 'docs.simplemachines.org';
    $context['doc_scripturl'] = 'http://docs.simplemachines.org/index.php';
    // Set all the parameters search might expect.
    $postVars = array('search' => $context['search_term']);
    // Encode the search data.
    foreach ($postVars as $k => $v) {
        $postVars[$k] = urlencode($k) . '=' . urlencode($v);
    }
    // This is what we will send.
    $postVars = implode('&', $postVars);
    // Get the results from the doc site.
    require_once $sourcedir . '/lib/Subs-Package.php';
    $search_results = fetch_web_data($context['doc_scripturl'] . '?action=search2&xml', $postVars);
    // If we didn't get any xml back we are in trouble - perhaps the doc site is overloaded?
    if (!$search_results || preg_match('~<' . '\\?xml\\sversion="\\d+\\.\\d+"\\sencoding=".+?"\\?' . '>\\s*(<smf>.+?</smf>)~is', $search_results, $matches) != true) {
        fatal_lang_error('cannot_connect_doc_site');
    }
    $search_results = $matches[1];
    // Otherwise we simply walk through the XML and stick it in context for display.
    $context['search_results'] = array();
    loadClassFile('Class-Package.php');
    // Get the results loaded into an array for processing!
    $results = new xmlArray($search_results, false);
    // Move through the smf layer.
    if (!$results->exists('smf')) {
        fatal_lang_error('cannot_connect_doc_site');
    }
    $results = $results->path('smf[0]');
    // Are there actually some results?
    if (!$results->exists('noresults') && !$results->exists('results')) {
        fatal_lang_error('cannot_connect_doc_site');
    } elseif ($results->exists('results')) {
        foreach ($results->set('results/result') as $result) {
            if (!$result->exists('messages')) {
                continue;
            }
            $context['search_results'][$result->fetch('id')] = array('topic_id' => $result->fetch('id'), 'relevance' => $result->fetch('relevance'), 'board' => array('id' => $result->fetch('board/id'), 'name' => $result->fetch('board/name'), 'href' => $result->fetch('board/href')), 'category' => array('id' => $result->fetch('category/id'), 'name' => $result->fetch('category/name'), 'href' => $result->fetch('category/href')), 'messages' => array());
            // Add the messages.
            foreach ($result->set('messages/message') as $message) {
                $context['search_results'][$result->fetch('id')]['messages'][] = array('id' => $message->fetch('id'), 'subject' => $message->fetch('subject'), 'body' => $message->fetch('body'), 'time' => $message->fetch('time'), 'timestamp' => $message->fetch('timestamp'), 'start' => $message->fetch('start'), 'author' => array('id' => $message->fetch('author/id'), 'name' => $message->fetch('author/name'), 'href' => $message->fetch('author/href')));
            }
        }
    }
}
Exemplo n.º 7
0
function parseMobRequest()
{
    global $context, $scripturl, $sourcedir, $mobsettings, $user_info, $modSettings;
    $ver = phpversion();
    if ($ver[0] >= 5) {
        $data = file_get_contents('php://input');
    } else {
        $data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
    }
    $data = str_replace('<param><value><boolean>false</boolean></value></param>', '<param><value><boolean>0</boolean></value></param>', $data);
    require_once $sourcedir . '/Subs-Package.php';
    $context['mob_request'] = array();
    $context['mob_response'] = array('encoding' => 'none');
    // We got compression buddy?
    if (isset($_SERVER['HTTP_CONTENT_ENCODING']) && function_exists('gzinflate')) {
        if ($_SERVER['HTTP_CONTENT_ENCODING'] == 'x-gzip' && !($data = @gzinflate(substr($data, 10)))) {
            createErrorResponse('server_decompress_failed', '', 'xmlrpc');
        } elseif ($_SERVER['HTTP_CONTENT_ENCODING'] == 'x-deflate' && !($data = @gzuncompress($data))) {
            createErrorResponse('server_decompress_failed', '', 'xmlrpc');
        } else {
            createErrorResponse('server_cannot_decompress', '', 'xmlrpc');
        }
    } elseif (isset($_SERVER['HTTP_CONTENT_ENCODING'])) {
        createErrorResponse('server_cannot_decompress', '', 'xmlrpc');
    }
    // Innitialize the SMF XML handler
    $xmlHandler = new xmlArray($data, true);
    // Get the method name
    if (!($context['mob_request']['method'] = $xmlHandler->fetch('methodCall/methodName'))) {
        createErrorResponse('unknown_method', '', 'xmlrpc');
    }
    // Are we closed?
    if ($context['mob_request']['method'] != 'login' && $context['mob_request']['method'] != 'get_config') {
        if (!empty($context['in_maintenance']) && !$user_info['is_admin']) {
            createErrorResponse(5, ' due to maintenance');
        } elseif (empty($modSettings['allow_guestAccess']) && $user_info['is_guest'] && $context['mob_request']['method'] != 'sign_in') {
            createErrorResponse(21);
        }
    }
    // Get the parameters
    $context['mob_request']['params'] = array();
    if ($xmlHandler->exists('methodCall/params')) {
        foreach ($xmlHandler->set('methodCall/params/param') as $parameter) {
            // Lame workdarround for create_message
            if (($context['mob_request']['method'] == 'create_message' || $context['mob_request']['method'] == 'new_topic' || $context['mob_request']['method'] == 'reply_post') && $parameter->exists('value/value')) {
                if (!$parameter->exists('value/value[1]')) {
                    $value = $parameter->to_array();
                    $values = array(0 => array($value['value']['base64'], 'base64'));
                } else {
                    $values = array();
                    foreach ($parameter->set('value/value') as $value) {
                        $value = $value->to_array();
                        $values[] = array($value['base64'], 'base64');
                    }
                }
                $context['mob_request']['params'][] = $values;
            } else {
                $parameter = $parameter->to_array();
                $keys = array_keys($parameter['value']);
                $values = array_values($parameter['value']);
                $context['mob_request']['params'][] = array($values[0], $keys[0]);
            }
        }
    }
}
Exemplo n.º 8
0
function parseModification($file, $testing = true, $undo = false)
{
    global $boarddir, $sourcedir, $settings, $txt, $modSettings, $package_ftp;
    @set_time_limit(600);
    $xml = new xmlArray(strtr($file, array("\r" => '')));
    $actions = array();
    $everything_found = true;
    if (!$xml->exists('modification') || !$xml->exists('modification/file')) {
        $actions[] = array('type' => 'error', 'filename' => '-', 'debug' => $txt['package_modification_malformed']);
        return $actions;
    }
    $files = $xml->set('modification/file');
    foreach ($files as $file) {
        $working_file = parse_path(trim($file->fetch('@name')));
        if ($working_file[0] != '/' && $working_file[1] != ':') {
            trigger_error('parseModification(): The filename \'' . $working_file . '\' is not a full path!', E_USER_WARNING);
            $working_file = $boarddir . '/' . $working_file;
        }
        // Doesn't exist - give an error or what?
        if (!file_exists($working_file) && (!$file->exists('@error') || !in_array(trim($file->fetch('@error')), array('ignore', 'skip')))) {
            $actions[] = array('type' => 'missing', 'filename' => $working_file, 'debug' => $txt['package_modification_missing']);
            $everything_found = false;
            continue;
        } elseif (!file_exists($working_file) && $file->exists('@error') && trim($file->fetch('@error')) === 'skip') {
            $actions[] = array('type' => 'skipping', 'filename' => $working_file);
            continue;
        } elseif (!file_exists($working_file)) {
            $working_data = '';
        } else {
            $working_data = str_replace("\r", '', package_get_contents($working_file));
        }
        $actions[] = array('type' => 'opened', 'filename' => $working_file);
        $operations = $file->exists('operation') ? $file->set('operation') : array();
        foreach ($operations as $operation) {
            // Convert operation to an array.
            $actual_operation = array('searches' => array(), 'error' => $operation->exists('@error') && in_array(trim($operation->fetch('@error')), array('ignore', 'fatal', 'required')) ? trim($operation->fetch('@error')) : 'fatal');
            // The 'add' parameter is used for all searches in this operation.
            $add = $operation->exists('add') ? $operation->fetch('add') : '';
            // Grab all search items of this operation (in most cases just 1).
            $searches = $operation->set('search');
            foreach ($searches as $i => $search) {
                $actual_operation['searches'][] = array('position' => $search->exists('@position') && in_array(trim($search->fetch('@position')), array('before', 'after', 'replace', 'end')) ? trim($search->fetch('@position')) : 'replace', 'is_reg_exp' => $search->exists('@regexp') && trim($search->fetch('@regexp')) === 'true', 'loose_whitespace' => $search->exists('@whitespace') && trim($search->fetch('@whitespace')) === 'loose', 'search' => $search->fetch('.'), 'add' => $add, 'preg_search' => '', 'preg_replace' => '');
            }
            // At least one search should be defined.
            if (empty($actual_operation['searches'])) {
                $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $search['search']);
                // Skip to the next operation.
                continue;
            }
            // Reverse the operations in case of undoing stuff.
            if ($undo) {
                foreach ($actual_operation['searches'] as $i => $search) {
                    // Reverse modification of regular expressions are not allowed.
                    if ($search['is_reg_exp']) {
                        if ($actual_operation['error'] === 'fatal') {
                            $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $search['search']);
                        }
                        // Continue to the next operation.
                        continue 2;
                    }
                    // The replacement is now the search subject...
                    if ($search['position'] === 'replace' || $search['position'] === 'end') {
                        $actual_operation['searches'][$i]['search'] = $search['add'];
                    } else {
                        // Reversing a before/after modification becomes a replacement.
                        $actual_operation['searches'][$i]['position'] = 'replace';
                        if ($search['position'] === 'before') {
                            $actual_operation['searches'][$i]['search'] .= $search['add'];
                        } elseif ($search['position'] === 'after') {
                            $actual_operation['searches'][$i]['search'] = $search['add'] . $search['search'];
                        }
                    }
                    // ...and the search subject is now the replacement.
                    $actual_operation['searches'][$i]['add'] = $search['search'];
                }
            }
            // Sort the search list so the replaces come before the add before/after's.
            if (count($actual_operation['searches']) !== 1) {
                $replacements = array();
                foreach ($actual_operation['searches'] as $i => $search) {
                    if ($search['position'] === 'replace') {
                        $replacements[] = $search;
                        unset($actual_operation['searches'][$i]);
                    }
                }
                $actual_operation['searches'] = array_merge($replacements, $actual_operation['searches']);
            }
            // Create regular expression replacements from each search.
            foreach ($actual_operation['searches'] as $i => $search) {
                // Not much needed if the search subject is already a regexp.
                if ($search['is_reg_exp']) {
                    $actual_operation['searches'][$i]['preg_search'] = $search['search'];
                } else {
                    // Make the search subject fit into a regular expression.
                    $actual_operation['searches'][$i]['preg_search'] = preg_quote($search['search'], '~');
                    // Using 'loose', a random amount of tabs and spaces may be used.
                    if ($search['loose_whitespace']) {
                        $actual_operation['searches'][$i]['preg_search'] = preg_replace('~[ \\t]+~', '[ \\t]+', $actual_operation['searches'][$i]['preg_search']);
                    }
                }
                // Shuzzup.  This is done so we can safely use a regular expression. ($0 is bad!!)
                $actual_operation['searches'][$i]['preg_replace'] = strtr($search['add'], array('$' => '[$PACK' . 'AGE1$]', '\\' => '[$PACK' . 'AGE2$]'));
                // Before, so the replacement comes after the search subject :P
                if ($search['position'] === 'before') {
                    $actual_operation['searches'][$i]['preg_search'] = '(' . $actual_operation['searches'][$i]['preg_search'] . ')';
                    $actual_operation['searches'][$i]['preg_replace'] = '$1' . $actual_operation['searches'][$i]['preg_replace'];
                } elseif ($search['position'] === 'after') {
                    $actual_operation['searches'][$i]['preg_search'] = '(' . $actual_operation['searches'][$i]['preg_search'] . ')';
                    $actual_operation['searches'][$i]['preg_replace'] .= '$1';
                } elseif ($search['position'] === 'end') {
                    if ($undo) {
                        $actual_operation['searches'][$i]['preg_replace'] = '';
                    } else {
                        $actual_operation['searches'][$i]['preg_search'] = '(\\n\\?\\>)?$';
                        $actual_operation['searches'][$i]['preg_replace'] .= '$1';
                    }
                }
                // Testing 1, 2, 3...
                $failed = preg_match('~' . $actual_operation['searches'][$i]['preg_search'] . '~s', $working_data) === 0;
                // Nope, search pattern not found.
                if ($failed && $actual_operation['error'] === 'fatal') {
                    $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $actual_operation['searches'][$i]['preg_search']);
                    $everything_found = false;
                    continue;
                } elseif (!$failed && $actual_operation['error'] === 'required') {
                    $actions[] = array('type' => 'failure', 'filename' => $working_file, 'search' => $actual_operation['searches'][$i]['preg_search']);
                    $everything_found = false;
                    continue;
                }
                // Replace it into nothing? That's not an option...unless it's an undoing end.
                if ($search['add'] === '' && ($search['position'] !== 'end' || !$undo)) {
                    continue;
                }
                // Finally, we're doing some replacements.
                $working_data = preg_replace('~' . $actual_operation['searches'][$i]['preg_search'] . '~s', $actual_operation['searches'][$i]['preg_replace'], $working_data, 1);
                $actions[] = array('type' => 'replace', 'filename' => $working_file, 'search' => $actual_operation['searches'][$i]['preg_search'], 'replace' => $actual_operation['searches'][$i]['preg_replace']);
            }
        }
        // Fix any little helper symbols ;).
        $working_data = strtr($working_data, array('[$PACK' . 'AGE1$]' => '$', '[$PACK' . 'AGE2$]' => '\\'));
        package_chmod($working_file);
        if (file_exists($working_file) && !is_writable($working_file) || !file_exists($working_file) && !is_writable(dirname($working_file))) {
            $actions[] = array('type' => 'chmod', 'filename' => $working_file);
        }
        if (basename($working_file) == 'Settings_bak.php') {
            continue;
        }
        if (!$testing && !empty($modSettings['package_make_backups']) && file_exists($working_file)) {
            // No, no, not Settings.php!
            if (basename($working_file) == 'Settings.php') {
                @copy($working_file, dirname($working_file) . '/Settings_bak.php');
            } else {
                @copy($working_file, $working_file . '~');
            }
        }
        // Always call this, even if in testing, because it won't really be written in testing mode.
        package_put_contents($working_file, $working_data, $testing);
        $actions[] = array('type' => 'saved', 'filename' => $working_file);
    }
    $actions[] = array('type' => 'result', 'status' => $everything_found);
    return $actions;
}
Exemplo n.º 9
0
    /**
     *
     */
    public static function loadArcade($mode = 'normal', $index = '')
    {
        global $db_prefix, $scripturl, $txt, $modSettings, $context, $settings, $sourcedir, $user_info;
        global $smcFunc, $boarddir;
        static $loaded = false;
        if (!empty($loaded)) {
            return;
        }
        spl_autoload_register(array('SMFArcade', 'autoload'));
        $loaded = true;
        $context['arcade'] = array();
        require_once $sourcedir . '/Subs-Arcade.php';
        // Load language
        loadLanguage('Arcade');
        // Permission query
        arcadePermissionQuery();
        // Normal mode
        if ($mode == 'normal' || $mode == 'arena') {
            if (empty($modSettings['arcadeEnabled'])) {
                return false;
            }
            loadTemplate('Arcade', array('forum', 'arcade'));
            $user_info['arcade_settings'] = loadArcadeSettings($user_info['id']);
            $context['games_per_page'] = !empty($user_info['arcade_settings']['gamesPerPage']) ? $user_info['arcade_settings']['gamesPerPage'] : $modSettings['gamesPerPage'];
            $context['scores_per_page'] = !empty($user_info['arcade_settings']['scoresPerPage']) ? $user_info['arcade_settings']['scoresPerPage'] : $modSettings['scoresPerPage'];
            if (!empty($modSettings['arcadeSkipJquery'])) {
                $context['html_headers'] .= '<script language="JavaScript" type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/arcade/jquery.js"></script>';
            }
            // Arcade javascript
            $context['html_headers'] .= '<script language="JavaScript" type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/arcade/main.js"></script>';
            // Add Arcade to link tree
            $context['linktree'][] = array('url' => $scripturl . '?action=arcade', 'name' => $txt['arcade']);
            // What I can do?
            $context['arcade']['can_play'] = allowedTo('arcade_play');
            $context['arcade']['can_favorite'] = !empty($modSettings['arcadeEnableFavorites']) && !$user_info['is_guest'];
            $context['arcade']['can_rate'] = !empty($modSettings['arcadeEnableRatings']) && !$user_info['is_guest'];
            $context['arcade']['can_submit'] = allowedTo('arcade_submit');
            $context['arcade']['can_comment_own'] = allowedTo('arcade_comment_own');
            $context['arcade']['can_comment_any'] = allowedTo('arcade_comment_any');
            $context['arcade']['can_admin_arcade'] = allowedTo('arcade_admin');
            $context['arcade']['can_create_match'] = allowedTo('arcade_create_match');
            $context['arcade']['can_join_match'] = allowedTo('arcade_join_match');
            // Or can I (do I have enought posts etc.)
            PostPermissionCheck();
            // Finally load Arcade Settings
            LoadArcadeSettings();
            if (!isset($_REQUEST['xml'])) {
                $context['template_layers'][] = 'Arcade';
            }
        } elseif ($mode == 'profile') {
            loadTemplate('ArcadeProfile', array('arcade', 'forum'));
        } elseif ($mode == 'admin') {
            loadTemplate('ArcadeAdmin');
            loadLanguage('ArcadeAdmin');
            $context['html_headers'] .= '
			<script language="JavaScript" type="text/javascript" src="' . $settings['default_theme_url'] . '/scripts/arcade.js"></script>';
            // Update games database?
            if (file_exists($boarddir . '/Games.xml')) {
                loadClassFile('Class-Package.php');
                $games = new xmlArray(file_get_contents($boarddir . '/Games.xml'));
                $database = $games->path('smf/database');
                $database = $database->to_array();
                $xmlGames = $games->set('smf/game');
                if (!empty($modSettings['arcadeGameDatabaseVersion']) && $modSettings['arcadeGameDatabaseVersion'] > $database['version']) {
                    break;
                }
                $games = array();
                foreach ($xmlGames as $game) {
                    $games[] = array($game->fetch('id'), $game->fetch('name'), $game->fetch('description'), $game->fetch('url/info'), $game->fetch('url/download'));
                }
                $smcFunc['db_insert']('replace', '{db_prefix}arcade_game_info', array('internal_name' => 'string', 'game_name' => 'string', 'description' => 'string', 'info_url' => 'string', 'download_url' => 'string'), $games, array('internal_name'));
                updateSettings(array('arcadeGameDatabaseVersion' => $database['version'], 'arcadeGameDatabaseUpdate' => $database['update']));
                @unlink($boarddir . '/Games.xml');
            }
            $context['template_layers'][] = 'ArcadeAdmin';
            $context['page_title'] = $txt['arcade_admin_title'];
        }
    }
Exemplo n.º 10
0
/**
 * Installs new themes, either from a gzip or copy of the default.
 * - puts themes in $boardurl/Themes.
 * - assumes the gzip has a root directory in it. (ie default.)
 * Requires admin_forum.
 * Accessed with ?action=admin;area=theme;sa=install.
 */
function ThemeInstall()
{
    global $sourcedir, $boarddir, $boardurl, $txt, $context, $settings, $modSettings, $smcFunc;
    checkSession('request');
    isAllowedTo('admin_forum');
    checkSession('request');
    require_once $sourcedir . '/Subs-Package.php';
    loadTemplate('Themes');
    if (isset($_GET['theme_id'])) {
        $result = $smcFunc['db_query']('', '
			SELECT value
			FROM {db_prefix}themes
			WHERE id_theme = {int:current_theme}
				AND id_member = {int:no_member}
				AND variable = {string:name}
			LIMIT 1', array('current_theme' => (int) $_GET['theme_id'], 'no_member' => 0, 'name' => 'name'));
        list($theme_name) = $smcFunc['db_fetch_row']($result);
        $smcFunc['db_free_result']($result);
        $context['sub_template'] = 'installed';
        $context['page_title'] = $txt['theme_installed'];
        $context['installed_theme'] = array('id' => (int) $_GET['theme_id'], 'name' => $theme_name);
        return;
    }
    if (!empty($_FILES['theme_gz']) && (!isset($_FILES['theme_gz']['error']) || $_FILES['theme_gz']['error'] != 4) || !empty($_REQUEST['theme_gz'])) {
        $method = 'upload';
    } elseif (isset($_REQUEST['theme_dir']) && rtrim(realpath($_REQUEST['theme_dir']), '/\\') != realpath($boarddir . '/Themes') && file_exists($_REQUEST['theme_dir'])) {
        $method = 'path';
    } else {
        $method = 'copy';
    }
    if (!empty($_REQUEST['copy']) && $method == 'copy') {
        // Hopefully the themes directory is writable, or we might have a problem.
        if (!is_writable($boarddir . '/Themes')) {
            fatal_lang_error('theme_install_write_error', 'critical');
        }
        $theme_dir = $boarddir . '/Themes/' . preg_replace('~[^A-Za-z0-9_\\- ]~', '', $_REQUEST['copy']);
        umask(0);
        mkdir($theme_dir, 0777);
        @set_time_limit(600);
        if (function_exists('apache_reset_timeout')) {
            @apache_reset_timeout();
        }
        // Create subdirectories for css and javascript files.
        mkdir($theme_dir . '/css', 0777);
        mkdir($theme_dir . '/scripts', 0777);
        // Copy over the default non-theme files.
        $to_copy = array('/index.php', '/index.template.php', '/css/index.css', '/css/rtl.css', '/scripts/theme.js');
        foreach ($to_copy as $file) {
            copy($settings['default_theme_dir'] . $file, $theme_dir . $file);
            @chmod($theme_dir . $file, 0777);
        }
        // And now the entire images directory!
        copytree($settings['default_theme_dir'] . '/images', $theme_dir . '/images');
        package_flush_cache();
        $theme_name = $_REQUEST['copy'];
        $images_url = $boardurl . '/Themes/' . basename($theme_dir) . '/images';
        $theme_dir = realpath($theme_dir);
        // Lets get some data for the new theme.
        $request = $smcFunc['db_query']('', '
			SELECT variable, value
			FROM {db_prefix}themes
			WHERE variable IN ({string:theme_templates}, {string:theme_layers})
				AND id_member = {int:no_member}
				AND id_theme = {int:default_theme}', array('no_member' => 0, 'default_theme' => 1, 'theme_templates' => 'theme_templates', 'theme_layers' => 'theme_layers'));
        while ($row = $smcFunc['db_fetch_assoc']($request)) {
            if ($row['variable'] == 'theme_templates') {
                $theme_templates = $row['value'];
            } elseif ($row['variable'] == 'theme_layers') {
                $theme_layers = $row['value'];
            } else {
                continue;
            }
        }
        $smcFunc['db_free_result']($request);
        // Lets add a theme_info.xml to this theme.
        $xml_info = '<' . '?xml version="1.0"?' . '>
<theme-info xmlns="http://www.simplemachines.org/xml/theme-info" xmlns:smf="http://www.simplemachines.org/">
	<!-- For the id, always use something unique - put your name, a colon, and then the package name. -->
	<id>smf:' . $smcFunc['strtolower'](str_replace(array(' '), '_', $_REQUEST['copy'])) . '</id>
	<version>' . $modSettings['smfVersion'] . '</version>
	<!-- Theme name, used purely for aesthetics. -->
	<name>' . $_REQUEST['copy'] . '</name>
	<!-- Author: your email address or contact information. The name attribute is optional. -->
	<author name="Simple Machines">info@simplemachines.org</author>
	<!-- Website... where to get updates and more information. -->
	<website>http://www.simplemachines.org/</website>
	<!-- Template layers to use, defaults to "html,body". -->
	<layers>' . (empty($theme_layers) ? 'html,body' : $theme_layers) . '</layers>
	<!-- Templates to load on startup. Default is "index". -->
	<templates>' . (empty($theme_templates) ? 'index' : $theme_templates) . '</templates>
	<!-- Base this theme off another? Default is blank, or no. It could be "default". -->
	<based-on></based-on>
</theme-info>';
        // Now write it.
        $fp = @fopen($theme_dir . '/theme_info.xml', 'w+');
        if ($fp) {
            fwrite($fp, $xml_info);
            fclose($fp);
        }
    } elseif (isset($_REQUEST['theme_dir']) && $method == 'path') {
        if (!is_dir($_REQUEST['theme_dir']) || !file_exists($_REQUEST['theme_dir'] . '/theme_info.xml')) {
            fatal_lang_error('theme_install_error', false);
        }
        $theme_name = basename($_REQUEST['theme_dir']);
        $theme_dir = $_REQUEST['theme_dir'];
    } elseif ($method = 'upload') {
        // Hopefully the themes directory is writable, or we might have a problem.
        if (!is_writable($boarddir . '/Themes')) {
            fatal_lang_error('theme_install_write_error', 'critical');
        }
        // This happens when the admin session is gone and the user has to login again
        if (empty($_FILES['theme_gz']) && empty($_REQUEST['theme_gz'])) {
            redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']);
        }
        // Set the default settings...
        $theme_name = strtok(basename(isset($_FILES['theme_gz']) ? $_FILES['theme_gz']['name'] : $_REQUEST['theme_gz']), '.');
        $theme_name = preg_replace(array('/\\s/', '/\\.[\\.]+/', '/[^\\w_\\.\\-]/'), array('_', '.', ''), $theme_name);
        $theme_dir = $boarddir . '/Themes/' . $theme_name;
        if (isset($_FILES['theme_gz']) && is_uploaded_file($_FILES['theme_gz']['tmp_name']) && (ini_get('open_basedir') != '' || file_exists($_FILES['theme_gz']['tmp_name']))) {
            $extracted = read_tgz_file($_FILES['theme_gz']['tmp_name'], $boarddir . '/Themes/' . $theme_name, false, true);
        } elseif (isset($_REQUEST['theme_gz'])) {
            // Check that the theme is from simplemachines.org, for now... maybe add mirroring later.
            if (preg_match('~^http://[\\w_\\-]+\\.simplemachines\\.org/~', $_REQUEST['theme_gz']) == 0 || strpos($_REQUEST['theme_gz'], 'dlattach') !== false) {
                fatal_lang_error('not_on_simplemachines');
            }
            $extracted = read_tgz_file($_REQUEST['theme_gz'], $boarddir . '/Themes/' . $theme_name, false, true);
        } else {
            redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']);
        }
    }
    // Something go wrong?
    if ($theme_dir != '' && basename($theme_dir) != 'Themes') {
        // Defaults.
        $install_info = array('theme_url' => $boardurl . '/Themes/' . basename($theme_dir), 'images_url' => isset($images_url) ? $images_url : $boardurl . '/Themes/' . basename($theme_dir) . '/images', 'theme_dir' => $theme_dir, 'name' => $theme_name);
        if (file_exists($theme_dir . '/theme_info.xml')) {
            $theme_info = file_get_contents($theme_dir . '/theme_info.xml');
            // Parse theme-info.xml into an xmlArray.
            require_once $sourcedir . '/Class-Package.php';
            $theme_info_xml = new xmlArray($theme_info);
            // @todo Error message of some sort?
            if (!$theme_info_xml->exists('theme-info[0]')) {
                return 'package_get_error_packageinfo_corrupt';
            }
            $theme_info_xml = $theme_info_xml->path('theme-info[0]');
            $theme_info_xml = $theme_info_xml->to_array();
            $xml_elements = array('name' => 'name', 'theme_layers' => 'layers', 'theme_templates' => 'templates', 'based_on' => 'based-on');
            foreach ($xml_elements as $var => $name) {
                if (!empty($theme_info_xml[$name])) {
                    $install_info[$var] = $theme_info_xml[$name];
                }
            }
            if (!empty($theme_info_xml['images'])) {
                $install_info['images_url'] = $install_info['theme_url'] . '/' . $theme_info_xml['images'];
                $explicit_images = true;
            }
            if (!empty($theme_info_xml['extra'])) {
                $install_info += unserialize($theme_info_xml['extra']);
            }
        }
        if (isset($install_info['based_on'])) {
            if ($install_info['based_on'] == 'default') {
                $install_info['theme_url'] = $settings['default_theme_url'];
                $install_info['images_url'] = $settings['default_images_url'];
            } elseif ($install_info['based_on'] != '') {
                $install_info['based_on'] = preg_replace('~[^A-Za-z0-9\\-_ ]~', '', $install_info['based_on']);
                $request = $smcFunc['db_query']('', '
					SELECT th.value AS base_theme_dir, th2.value AS base_theme_url' . (!empty($explicit_images) ? '' : ', th3.value AS images_url') . '
					FROM {db_prefix}themes AS th
						INNER JOIN {db_prefix}themes AS th2 ON (th2.id_theme = th.id_theme
							AND th2.id_member = {int:no_member}
							AND th2.variable = {string:theme_url})' . (!empty($explicit_images) ? '' : '
						INNER JOIN {db_prefix}themes AS th3 ON (th3.id_theme = th.id_theme
							AND th3.id_member = {int:no_member}
							AND th3.variable = {string:images_url})') . '
					WHERE th.id_member = {int:no_member}
						AND (th.value LIKE {string:based_on} OR th.value LIKE {string:based_on_path})
						AND th.variable = {string:theme_dir}
					LIMIT 1', array('no_member' => 0, 'theme_url' => 'theme_url', 'images_url' => 'images_url', 'theme_dir' => 'theme_dir', 'based_on' => '%/' . $install_info['based_on'], 'based_on_path' => '%' . "\\" . $install_info['based_on']));
                $temp = $smcFunc['db_fetch_assoc']($request);
                $smcFunc['db_free_result']($request);
                // @todo An error otherwise?
                if (is_array($temp)) {
                    $install_info = $temp + $install_info;
                    if (empty($explicit_images) && !empty($install_info['base_theme_url'])) {
                        $install_info['theme_url'] = $install_info['base_theme_url'];
                    }
                }
            }
            unset($install_info['based_on']);
        }
        // Find the newest id_theme.
        $result = $smcFunc['db_query']('', '
			SELECT MAX(id_theme)
			FROM {db_prefix}themes', array());
        list($id_theme) = $smcFunc['db_fetch_row']($result);
        $smcFunc['db_free_result']($result);
        // This will be theme number...
        $id_theme++;
        $inserts = array();
        foreach ($install_info as $var => $val) {
            $inserts[] = array($id_theme, $var, $val);
        }
        if (!empty($inserts)) {
            $smcFunc['db_insert']('insert', '{db_prefix}themes', array('id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), $inserts, array('id_theme', 'variable'));
        }
        updateSettings(array('knownThemes' => strtr($modSettings['knownThemes'] . ',' . $id_theme, array(',,' => ','))));
    }
    redirectexit('action=admin;area=theme;sa=install;theme_id=' . $id_theme . ';' . $context['session_var'] . '=' . $context['session_id']);
}