/** * Allow the admin to reset permissions on files. */ public function action_perms() { global $context, $txt, $modSettings, $package_ftp; // Let's try and be good, yes? checkSession('get'); // If we're restoring permissions this is just a pass through really. if (isset($_GET['restore'])) { create_chmod_control(array(), array(), true); fatal_lang_error('no_access', false); } // This is a time and memory eating ... setMemoryLimit('128M'); @set_time_limit(600); // Load up some FTP stuff. create_chmod_control(); if (empty($package_ftp) && !isset($_POST['skip_ftp'])) { require_once SUBSDIR . '/FtpConnection.class.php'; $ftp = new Ftp_Connection(null); list($username, $detect_path, $found_path) = $ftp->detect_path(BOARDDIR); $context['package_ftp'] = array('server' => isset($modSettings['package_server']) ? $modSettings['package_server'] : 'localhost', 'port' => isset($modSettings['package_port']) ? $modSettings['package_port'] : '21', 'username' => empty($username) ? isset($modSettings['package_username']) ? $modSettings['package_username'] : '' : $username, 'path' => $detect_path, 'form_elements_only' => true); } else { $context['ftp_connected'] = true; } // Define the template. $context['page_title'] = $txt['package_file_perms']; $context['sub_template'] = 'file_permissions'; // Define what files we're interested in, as a tree. $context['file_tree'] = array(strtr(BOARDDIR, array('\\' => '/')) => array('type' => 'dir', 'contents' => array('agreement.txt' => array('type' => 'file', 'writable_on' => 'standard'), 'Settings.php' => array('type' => 'file', 'writable_on' => 'restrictive'), 'Settings_bak.php' => array('type' => 'file', 'writable_on' => 'restrictive'), 'attachments' => array('type' => 'dir', 'writable_on' => 'restrictive'), 'avatars' => array('type' => 'dir', 'writable_on' => 'standard'), 'cache' => array('type' => 'dir', 'writable_on' => 'restrictive'), 'custom_avatar_dir' => array('type' => 'dir', 'writable_on' => 'restrictive'), 'smileys' => array('type' => 'dir_recursive', 'writable_on' => 'standard'), 'sources' => array('type' => 'dir', 'list_contents' => true, 'writable_on' => 'standard'), 'themes' => array('type' => 'dir_recursive', 'writable_on' => 'standard', 'contents' => array('default' => array('type' => 'dir_recursive', 'list_contents' => true, 'contents' => array('languages' => array('type' => 'dir', 'list_contents' => true))))), 'packages' => array('type' => 'dir', 'writable_on' => 'standard', 'contents' => array('temp' => array('type' => 'dir'), 'backup' => array('type' => 'dir'), 'installed.list' => array('type' => 'file', 'writable_on' => 'standard')))))); // Directories that can move. if (substr(SOURCEDIR, 0, strlen(BOARDDIR)) != BOARDDIR) { unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['sources']); $context['file_tree'][strtr(SOURCEDIR, array('\\' => '/'))] = array('type' => 'dir', 'list_contents' => true, 'writable_on' => 'standard'); } // Moved the cache? if (substr(CACHEDIR, 0, strlen(BOARDDIR)) != BOARDDIR) { unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['cache']); $context['file_tree'][strtr(CACHEDIR, array('\\' => '/'))] = array('type' => 'dir', 'list_contents' => false, 'writable_on' => 'restrictive'); } // Are we using multiple attachment directories? if (!empty($modSettings['currentAttachmentUploadDir'])) { unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['attachments']); if (!is_array($modSettings['attachmentUploadDir'])) { $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']); } // @todo Should we suggest non-current directories be read only? foreach ($modSettings['attachmentUploadDir'] as $dir) { $context['file_tree'][strtr($dir, array('\\' => '/'))] = array('type' => 'dir', 'writable_on' => 'restrictive'); } } elseif (substr($modSettings['attachmentUploadDir'], 0, strlen(BOARDDIR)) != BOARDDIR) { unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['attachments']); $context['file_tree'][strtr($modSettings['attachmentUploadDir'], array('\\' => '/'))] = array('type' => 'dir', 'writable_on' => 'restrictive'); } if (substr($modSettings['smileys_dir'], 0, strlen(BOARDDIR)) != BOARDDIR) { unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['smileys']); $context['file_tree'][strtr($modSettings['smileys_dir'], array('\\' => '/'))] = array('type' => 'dir_recursive', 'writable_on' => 'standard'); } if (substr($modSettings['avatar_directory'], 0, strlen(BOARDDIR)) != BOARDDIR) { unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['avatars']); $context['file_tree'][strtr($modSettings['avatar_directory'], array('\\' => '/'))] = array('type' => 'dir', 'writable_on' => 'standard'); } if (isset($modSettings['custom_avatar_dir']) && substr($modSettings['custom_avatar_dir'], 0, strlen(BOARDDIR)) != BOARDDIR) { unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['custom_avatar_dir']); $context['file_tree'][strtr($modSettings['custom_avatar_dir'], array('\\' => '/'))] = array('type' => 'dir', 'writable_on' => 'restrictive'); } // Load up any custom themes. require_once SUBSDIR . '/Themes.subs.php'; $themes = getCustomThemes(); foreach ($themes as $id => $theme) { // Skip the default if ($id == 1) { continue; } if (substr(strtolower(strtr($theme['theme_dir'], array('\\' => '/'))), 0, strlen(BOARDDIR) + 7) === strtolower(strtr(BOARDDIR, array('\\' => '/')) . '/themes')) { $context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['themes']['contents'][substr($theme['theme_dir'], strlen(BOARDDIR) + 8)] = array('type' => 'dir_recursive', 'list_contents' => true, 'contents' => array('languages' => array('type' => 'dir', 'list_contents' => true))); } else { $context['file_tree'][strtr($theme['theme_dir'], array('\\' => '/'))] = array('type' => 'dir_recursive', 'list_contents' => true, 'contents' => array('languages' => array('type' => 'dir', 'list_contents' => true))); } } // If we're submitting then let's move on to another function to keep things cleaner.. if (isset($_POST['action_changes'])) { return $this->action_perms_save(); } $context['look_for'] = array(); // Are we looking for a particular tree - normally an expansion? if (!empty($_REQUEST['find'])) { $context['look_for'][] = base64_decode($_REQUEST['find']); } // Only that tree? $context['only_find'] = isset($_GET['xml']) && !empty($_REQUEST['onlyfind']) ? $_REQUEST['onlyfind'] : ''; if ($context['only_find']) { $context['look_for'][] = $context['only_find']; } // Have we got a load of back-catalogue trees to expand from a submit etc? if (!empty($_GET['back_look'])) { $potententialTrees = unserialize(base64_decode($_GET['back_look'])); foreach ($potententialTrees as $tree) { $context['look_for'][] = $tree; } } // ... maybe posted? if (!empty($_POST['back_look'])) { $context['only_find'] = array_merge($context['only_find'], $_POST['back_look']); } $context['back_look_data'] = base64_encode(serialize(array_slice($context['look_for'], 0, 15))); // Are we finding more files than first thought? $context['file_offset'] = !empty($_REQUEST['fileoffset']) ? (int) $_REQUEST['fileoffset'] : 0; // Don't list more than this many files in a directory. $context['file_limit'] = 150; // How many levels shall we show? $context['default_level'] = empty($context['only_find']) ? 2 : 25; // This will be used if we end up catching XML data. $context['xml_data'] = array('roots' => array('identifier' => 'root', 'children' => array(array('value' => preg_replace('~[^A-Za-z0-9_\\-=:]~', ':-:', $context['only_find'])))), 'folders' => array('identifier' => 'folder', 'children' => array())); foreach ($context['file_tree'] as $path => $data) { // Run this directory. if (file_exists($path) && (empty($context['only_find']) || substr($context['only_find'], 0, strlen($path)) == $path)) { // Get the first level down only. fetchPerms__recursive($path, $context['file_tree'][$path], 1); $context['file_tree'][$path]['perms'] = array('chmod' => @is_writable($path), 'perms' => @fileperms($path)); } else { unset($context['file_tree'][$path]); } } // Is this actually xml? if (isset($_GET['xml'])) { loadTemplate('Xml'); $context['sub_template'] = 'generic_xml'; Template_Layers::getInstance()->removeAll(); } }
function fetchPerms__recursive($path, &$data, $level) { global $context; $isLikelyPath = false; foreach ($context['look_for'] as $possiblePath) { if (substr($possiblePath, 0, strlen($path)) == $path) { $isLikelyPath = true; } } // Is this where we stop? if (isset($_GET['xml']) && !empty($context['look_for']) && !$isLikelyPath) { return; } elseif ($level > $context['default_level'] && !$isLikelyPath) { return; } // Are we actually interested in saving this data? $save_data = empty($context['only_find']) || $context['only_find'] == $path; //!!! Shouldn't happen - but better error message? if (!is_dir($path)) { fatal_lang_error('no_access', false); } // This is where we put stuff we've found for sorting. $foundData = array('files' => array(), 'folders' => array()); $dh = opendir($path); while ($entry = readdir($dh)) { // Some kind of file? if (!is_dir($path . '/' . $entry)) { // Are we listing PHP files in this directory? if ($save_data && !empty($data['list_contents']) && substr($entry, -4) == '.php') { $foundData['files'][$entry] = true; } elseif ($save_data && isset($data['contents'][$entry])) { $foundData['files'][$entry] = true; } } elseif ($entry != '.' && $entry != '..') { // Going further? if (!empty($data['type']) && $data['type'] == 'dir_recursive' || isset($data['contents'][$entry]) && (!empty($data['contents'][$entry]['list_contents']) || !empty($data['contents'][$entry]['type']) && $data['contents'][$entry]['type'] == 'dir_recursive')) { if (!isset($data['contents'][$entry])) { $foundData['folders'][$entry] = 'dir_recursive'; } else { $foundData['folders'][$entry] = true; } // If this wasn't expected inherit the recusiveness... if (!isset($data['contents'][$entry])) { // We need to do this as we will be going all recursive. $data['contents'][$entry] = array('type' => 'dir_recursive'); } // Actually do the recursive stuff... fetchPerms__recursive($path . '/' . $entry, $data['contents'][$entry], $level + 1); } elseif (isset($data['contents'][$entry])) { $foundData['folders'][$entry] = true; } // Otherwise we stop here. } } closedir($dh); // Nothing to see here? if (!$save_data) { return; } // Now actually add the data, starting with the folders. ksort($foundData['folders']); foreach ($foundData['folders'] as $folder => $type) { $additional_data = array('perms' => array('chmod' => @is_writable($path . '/' . $folder), 'perms' => @fileperms($path . '/' . $folder))); if ($type !== true) { $additional_data['type'] = $type; } // If there's an offset ignore any folders in XML mode. if (isset($_GET['xml']) && $context['file_offset'] == 0) { $context['xml_data']['folders']['children'][] = array('attributes' => array('writable' => $additional_data['perms']['chmod'] ? 1 : 0, 'permissions' => substr(sprintf('%o', $additional_data['perms']['perms']), -4), 'folder' => 1, 'path' => $context['only_find'], 'level' => $level, 'more' => 0, 'offset' => $context['file_offset'], 'my_ident' => preg_replace('~[^A-Za-z0-9_\\-=:]~', ':-:', $context['only_find'] . '/' . $folder), 'ident' => preg_replace('~[^A-Za-z0-9_\\-=:]~', ':-:', $context['only_find'])), 'value' => $folder); } elseif (!isset($_GET['xml'])) { if (isset($data['contents'][$folder])) { $data['contents'][$folder] = array_merge($data['contents'][$folder], $additional_data); } else { $data['contents'][$folder] = $additional_data; } } } // Now we want to do a similar thing with files. ksort($foundData['files']); $counter = -1; foreach ($foundData['files'] as $file => $dummy) { $counter++; // Have we reached our offset? if ($context['file_offset'] > $counter) { continue; } // Gone too far? if ($counter > $context['file_offset'] + $context['file_limit']) { continue; } $additional_data = array('perms' => array('chmod' => @is_writable($path . '/' . $file), 'perms' => @fileperms($path . '/' . $file))); // XML? if (isset($_GET['xml'])) { $context['xml_data']['folders']['children'][] = array('attributes' => array('writable' => $additional_data['perms']['chmod'] ? 1 : 0, 'permissions' => substr(sprintf('%o', $additional_data['perms']['perms']), -4), 'folder' => 0, 'path' => $context['only_find'], 'level' => $level, 'more' => $counter == $context['file_offset'] + $context['file_limit'] ? 1 : 0, 'offset' => $context['file_offset'], 'my_ident' => preg_replace('~[^A-Za-z0-9_\\-=:]~', ':-:', $context['only_find'] . '/' . $file), 'ident' => preg_replace('~[^A-Za-z0-9_\\-=:]~', ':-:', $context['only_find'])), 'value' => $file); } elseif ($counter != $context['file_offset'] + $context['file_limit']) { if (isset($data['contents'][$file])) { $data['contents'][$file] = array_merge($data['contents'][$file], $additional_data); } else { $data['contents'][$file] = $additional_data; } } } }