/** * This file allows the user to search the wiki documentation * for a little help. */ public function action_search_doc() { global $context; $context['doc_apiurl'] = 'https://github.com/elkarte/Elkarte/wiki/api.php'; $context['doc_scripturl'] = 'https://github.com/elkarte/Elkarte/wiki/'; // 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 SUBSDIR . '/Package.subs.php'; // Demo URL: // https://github.com/elkarte/Elkarte/wiki/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 SUBSDIR . '/XmlArray.class.php'; // Get the results loaded into an array for processing! $results = new Xml_Array($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')))); } } }
/** * Parses a xml-style modification file (file). * * @package Packages * @param string $file * @param bool $testing = true tells it the modifications shouldn't actually be saved. * @param bool $undo = false specifies that the modifications the file requests should be undone; this doesn't work with everything (regular expressions.) * @param mixed[] $theme_paths = array() * @return array an array of those changes made. */ function parseModification($file, $testing = true, $undo = false, $theme_paths = array()) { global $txt, $modSettings; @set_time_limit(600); require_once SUBSDIR . '/XmlArray.class.php'; $xml = new Xml_Array(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; }
/** * Installs new themes, either from a gzip or copy of the default. * * What it does: * - 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. * * @uses ManageThemes template */ public function action_install() { global $boardurl, $txt, $context, $settings, $modSettings; checkSession('request'); require_once SUBSDIR . '/Themes.subs.php'; require_once SUBSDIR . '/Package.subs.php'; loadTemplate('ManageThemes'); // Passed an ID, then the install is complete, lets redirect and show them if (isset($_GET['theme_id'])) { $_GET['theme_id'] = (int) $_GET['theme_id']; $context['sub_template'] = 'installed'; $context['page_title'] = $txt['theme_installed']; $context['installed_theme'] = array('id' => $_GET['theme_id'], 'name' => getThemeName($_GET['theme_id'])); return; } // How are we going to install this theme, from a dir, zip, copy of default? 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'; } // Copy the default theme? 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'); } // Make the new directory, standard characters only $theme_dir = BOARDDIR . '/themes/' . preg_replace('~[^A-Za-z0-9_\\- ]~', '', $_REQUEST['copy']); umask(0); mkdir($theme_dir, 0777); // Get some more time if we can @set_time_limit(600); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Create the subdirectories for css, javascript and font files. mkdir($theme_dir . '/css', 0777); mkdir($theme_dir . '/scripts', 0777); mkdir($theme_dir . '/webfonts', 0777); // Copy over the default non-theme files. $to_copy = array('/index.php', '/index.template.php', '/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 css, images and webfonts directories! copytree($settings['default_theme_dir'] . '/css', $theme_dir . '/css'); copytree($settings['default_theme_dir'] . '/images', $theme_dir . '/images'); copytree($settings['default_theme_dir'] . '/webfonts', $theme_dir . '/webfonts'); 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 (default theme (1), default settings (0)). $theme_values = loadThemeOptionsInto(1, 0, array(), array('theme_templates', 'theme_layers')); // Lets add a theme_info.xml to this theme. write_theme_info($_REQUEST['copy'], $modSettings['elkVersion'], $theme_dir, $theme_values); } 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']))) { read_tgz_file($_FILES['theme_gz']['tmp_name'], BOARDDIR . '/themes/' . $theme_name, false, true); } elseif (isset($_REQUEST['theme_gz'])) { if (!isAuthorizedServer($_REQUEST['theme_gz'])) { fatal_lang_error('not_valid_server'); } 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']); } } else { fatal_lang_error('theme_install_general', false); } // 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); $explicit_images = false; if (file_exists($theme_dir . '/theme_info.xml')) { $theme_info = file_get_contents($theme_dir . '/theme_info.xml'); // Parse theme-info.xml into an Xml_Array. require_once SUBSDIR . '/XmlArray.class.php'; $theme_info_xml = new Xml_Array($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']); $temp = loadBasedOnTheme($install_info['based_on'], $explicit_images); // @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. $id_theme = nextTheme(); $inserts = array(); foreach ($install_info as $var => $val) { $inserts[] = array($id_theme, $var, $val); } if (!empty($inserts)) { addTheme($inserts); } 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']); }
/** * Gets a list of available languages from the mother ship * * - Will return a subset if searching, otherwise all available * * @package Languages * @return string */ function list_getLanguagesList() { global $forum_version, $context, $txt, $scripturl; // We're going to use this URL. // @todo no we are not, this needs to be changed - again $url = 'http://download.elkarte.net/fetch_language.php?version=' . urlencode(strtr($forum_version, array('ElkArte ' => ''))); // Load the class file and stick it into an array. require_once SUBSDIR . '/XmlArray.class.php'; $language_list = new Xml_Array(fetch_web_data($url), true); // Check that the site responded and that the language exists. if (!$language_list->exists('languages')) { $context['langfile_error'] = 'no_response'; } elseif (!$language_list->exists('languages/language')) { $context['langfile_error'] = 'no_files'; } else { $language_list = $language_list->path('languages[0]'); $lang_files = $language_list->set('language'); $languages = array(); foreach ($lang_files as $file) { // Were we searching? if (!empty($context['elk_search_term']) && strpos($file->fetch('name'), Util::strtolower($context['elk_search_term'])) === false) { continue; } $languages[] = array('id' => $file->fetch('id'), 'name' => Util::ucwords($file->fetch('name')), 'version' => $file->fetch('version'), 'utf8' => $txt['yes'], '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_elk_install'] . '</a>'); } if (empty($languages)) { $context['langfile_error'] = 'no_files'; } else { return $languages; } } }
/** * Browse a server's list of packages. * * - Accessed by action=admin;area=packageservers;sa=browse */ public function action_browse() { global $txt, $scripturl, $forum_version, $context; // Load our subs worker. require_once SUBSDIR . '/PackageServers.subs.php'; // Browsing the packages from a server if (isset($_GET['server'])) { if ($_GET['server'] == '') { redirectexit('action=admin;area=packageservers'); } $server = (int) $_GET['server']; // Query the server list to find the current server. $packageserver = fetchPackageServers($server); $url = $packageserver[0]['url']; $name = $packageserver[0]['name']; // If the server does not exist, dump out. if (empty($url)) { fatal_lang_error('couldnt_connect', 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 required 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['package_servers']; $context['confirm_message'] = sprintf($txt['package_confirm_view_package_content'], htmlspecialchars($_GET['absolute'], ENT_COMPAT, 'UTF-8')); $context['proceed_href'] = $scripturl . '?action=admin;area=packageservers;sa=browse;absolute=' . urlencode($_GET['absolute']) . ';confirm=' . $token; return; } } else { fatal_lang_error('couldnt_connect', false); } // 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')); } // Might take some time. @set_time_limit(600); // Read packages.xml and parse into Xml_Array. (the true tells it to trim things ;).) require_once SUBSDIR . '/XmlArray.class.php'; $listing = new Xml_Array(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 = Util::htmlspecialchars($listing->fetch('list-title')); } // Pick the correct template. $context['sub_template'] = 'package_list'; $context['page_title'] = $txt['package_servers'] . ($name != '' ? ' - ' . $name : ''); $context['package_server'] = $server; // By default we use an unordered list, unless there are no lists with more than one package. $context['list_type'] = 'ul'; // Load the installed packages // We'll figure out if what they select a package they already have installed. $instadds = loadInstalledPackages(); // Look through the list of installed mods... $installed_adds = array(); foreach ($instadds as $installed_add) { $installed_adds[$installed_add['package_id']] = $installed_add['version']; } // Get default author and email if they exist. if ($listing->exists('default-author')) { $default_author = Util::htmlspecialchars($listing->fetch('default-author')); if ($listing->exists('default-author/@email')) { $default_email = Util::htmlspecialchars($listing->fetch('default-author/@email')); } } // Get default web site if it exists. if ($listing->exists('default-website')) { $default_website = Util::htmlspecialchars($listing->fetch('default-website')); if ($listing->exists('default-website/@title')) { $default_title = Util::htmlspecialchars($listing->fetch('default-website/@title')); } } $the_version = strtr($forum_version, array('ElkArte ' => '')); if (!empty($_SESSION['version_emulate'])) { $the_version = $_SESSION['version_emulate']; } $packageNum = 0; $packageSection = 0; $sections = $listing->set('section'); foreach ($sections as $i => $section) { $context['package_list'][$packageSection] = array('title' => '', 'text' => '', 'items' => array()); $packages = $section->set('title|heading|text|remote|rule|modification|language|avatar-pack|theme|smiley-set'); foreach ($packages as $thisPackage) { $package = array('type' => $thisPackage->name()); if (in_array($package['type'], array('title', 'text'))) { $context['package_list'][$packageSection][$package['type']] = Util::htmlspecialchars($thisPackage->fetch('.')); } elseif (in_array($package['type'], array('heading', 'rule'))) { $package['name'] = Util::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://' || substr($thisPackage->fetch('@href'), 0, 8) !== 'https://')) { 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=admin;area=packageservers;sa=browse;absolute=' . $current_url; } else { $package['href'] = $scripturl . '?action=admin;area=packageservers;sa=browse;server=' . $context['package_server'] . ';relative=' . $current_url; } } else { $current_url = $thisPackage->fetch('@href'); $package['href'] = $scripturl . '?action=admin;area=packageservers;sa=browse;absolute=' . $current_url; } $package['name'] = Util::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['package_no_description']; } else { $package['description'] = parse_bbc(preg_replace('~\\[[/]?html\\]~i', '', Util::htmlspecialchars($package['description']))); } $package['is_installed'] = isset($installed_adds[$package['id']]); $package['is_current'] = $package['is_installed'] && $installed_adds[$package['id']] == $package['version']; $package['is_newer'] = $package['is_installed'] && $installed_adds[$package['id']] > $package['version']; // This package is either not installed, or installed but old. Is it supported on this version? 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'] = is_array($already_exists) && $already_exists['id'] == $package['id'] && $already_exists['version'] != $package['version']; $package['href'] = $url . '/' . $package['filename']; $package['name'] = Util::htmlspecialchars($package['name']); $package['link'] = '<a href="' . $package['href'] . '">' . $package['name'] . '</a>'; $package['download']['href'] = $scripturl . '?action=admin;area=packageservers;sa=download' . $server_att . ';package=' . $current_url . $package['filename'] . ($package['download_conflict'] ? ';conflict' : '') . ';' . $context['session_var'] . '=' . $context['session_id']; $package['download']['link'] = '<a href="' . $package['download']['href'] . '">' . $package['name'] . '</a>'; // Author name, email if ($thisPackage->exists('author') || isset($default_author)) { if ($thisPackage->exists('author/@email')) { $package['author']['email'] = $thisPackage->fetch('author/@email'); } elseif (isset($default_email)) { $package['author']['email'] = $default_email; } if ($thisPackage->exists('author') && $thisPackage->fetch('author') != '') { $package['author']['name'] = Util::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 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>'; } } // Author website if ($thisPackage->exists('website') || isset($default_website)) { if ($thisPackage->exists('website') && $thisPackage->exists('website/@title')) { $package['author']['website']['name'] = Util::htmlspecialchars($thisPackage->fetch('website/@title')); } elseif (isset($default_title)) { $package['author']['website']['name'] = $default_title; } elseif ($thisPackage->exists('website')) { $package['author']['website']['name'] = Util::htmlspecialchars($thisPackage->fetch('website')); } else { $package['author']['website']['name'] = $default_website; } if ($thisPackage->exists('website') && $thisPackage->fetch('website') != '') { $authorhomepage = Util::htmlspecialchars($thisPackage->fetch('website')); } else { $authorhomepage = $default_website; } if (stripos($authorhomepage, 'a href') === false) { $package['author']['website']['href'] = $authorhomepage; $package['author']['website']['link'] = '<a href="' . $authorhomepage . '">' . $package['author']['website']['name'] . '</a>'; } else { if (preg_match('/a href="(.+?)"/', $authorhomepage, $match) == 1) { $package['author']['website']['href'] = $match[1]; } else { $package['author']['website']['href'] = ''; } $package['author']['website']['link'] = $authorhomepage; } } 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; if (!in_array($package['type'], array('title', 'text'))) { $context['package_list'][$packageSection]['items'][] = $package; } if ($package['count'] > 1) { $context['list_type'] = 'ol'; } } $packageSection++; } // 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 $ps_id => $packageSection) { foreach ($packageSection['items'] as $i => $package) { if ($package['count'] == 0 || isset($package['can_install'])) { continue; } $context['package_list'][$ps_id]['items'][$i]['can_install'] = false; $packageInfo = getPackageInfo($url . '/' . $package['filename']); if (is_array($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'][$ps_id]['items'][$i]['can_install'] = true; break; } } } } } }