/** * Actually action the permission changes they want. */ public function action_perms_save() { global $context, $txt, $time_start, $package_ftp; umask(0); $timeout_limit = 5; $context['method'] = $_POST['method'] === 'individual' ? 'individual' : 'predefined'; $context['back_look_data'] = isset($_POST['back_look']) ? $_POST['back_look'] : array(); // Skipping use of FTP? if (empty($package_ftp)) { $context['skip_ftp'] = true; } // We'll start off in a good place, security. Make sure that if we're dealing with individual files that they seem in the right place. if ($context['method'] === 'individual') { // Only these path roots are legal. $legal_roots = array_keys($context['file_tree']); $context['custom_value'] = (int) $_POST['custom_value']; // Continuing? if (isset($_POST['toProcess'])) { $_POST['permStatus'] = unserialize(base64_decode($_POST['toProcess'])); } if (isset($_POST['permStatus'])) { $context['to_process'] = array(); $validate_custom = false; foreach ($_POST['permStatus'] as $path => $status) { // Nothing to see here? if ($status === 'no_change') { continue; } $legal = false; foreach ($legal_roots as $root) { if (substr($path, 0, strlen($root)) == $root) { $legal = true; } } if (!$legal) { continue; } // Check it exists. if (!file_exists($path)) { continue; } if ($status === 'custom') { $validate_custom = true; } // Now add it. $context['to_process'][$path] = $status; } $context['total_items'] = isset($_POST['totalItems']) ? (int) $_POST['totalItems'] : count($context['to_process']); // Make sure the chmod status is valid? if ($validate_custom) { if (preg_match('~^[4567][4567][4567]$~', $context['custom_value']) == false) { fatal_error($txt['chmod_value_invalid']); } } // Nothing to do? if (empty($context['to_process'])) { redirectexit('action=admin;area=packages;sa=perms' . (!empty($context['back_look_data']) ? ';back_look=' . base64_encode(serialize($context['back_look_data'])) : '') . ';' . $context['session_var'] . '=' . $context['session_id']); } } else { fatal_lang_error('no_access', false); } // Setup the custom value. $custom_value = octdec('0' . $context['custom_value']); // Start processing items. foreach ($context['to_process'] as $path => $status) { if (in_array($status, array('execute', 'writable', 'read'))) { package_chmod($path, $status); } elseif ($status == 'custom' && !empty($custom_value)) { // Use FTP if we have it. if (!empty($package_ftp) && !empty($_SESSION['pack_ftp'])) { $ftp_file = strtr($path, array($_SESSION['pack_ftp']['root'] => '')); $package_ftp->chmod($ftp_file, $custom_value); } else { @chmod($path, $custom_value); } } // This fish is fried... unset($context['to_process'][$path]); // See if we're out of time? if (time() - array_sum(explode(' ', $time_start)) > $timeout_limit) { pausePermsSave(); } } } else { $context['predefined_type'] = isset($_POST['predefined']) ? $_POST['predefined'] : 'restricted'; $context['total_items'] = isset($_POST['totalItems']) ? (int) $_POST['totalItems'] : 0; $context['directory_list'] = isset($_POST['dirList']) ? unserialize(base64_decode($_POST['dirList'])) : array(); $context['file_offset'] = isset($_POST['fileOffset']) ? (int) $_POST['fileOffset'] : 0; // Haven't counted the items yet? if (empty($context['total_items'])) { foreach ($context['file_tree'] as $path => $data) { if (is_dir($path)) { $context['directory_list'][$path] = 1; $context['total_items'] += $this->count_directories__recursive($path); $context['total_items']++; } } } // Have we built up our list of special files? if (!isset($_POST['specialFiles']) && $context['predefined_type'] != 'free') { $context['special_files'] = array(); foreach ($context['file_tree'] as $path => $data) { $this->build_special_files__recursive($path, $data); } } elseif ($context['predefined_type'] === 'free') { $context['special_files'] = array(); } else { $context['special_files'] = unserialize(base64_decode($_POST['specialFiles'])); } // Now we definitely know where we are, we need to go through again doing the chmod! foreach ($context['directory_list'] as $path => $dummy) { // Do the contents of the directory first. $dh = @opendir($path); $file_count = 0; $dont_chmod = false; while ($entry = readdir($dh)) { $file_count++; // Actually process this file? if (!$dont_chmod && !is_dir($path . '/' . $entry) && (empty($context['file_offset']) || $context['file_offset'] < $file_count)) { $status = $context['predefined_type'] === 'free' || isset($context['special_files'][$path . '/' . $entry]) ? 'writable' : 'execute'; package_chmod($path . '/' . $entry, $status); } // See if we're out of time? if (!$dont_chmod && time() - array_sum(explode(' ', $time_start)) > $timeout_limit) { $dont_chmod = true; // Make note of how far we have come so we restart at the right point $context['file_offset'] = $file_count; break; } } closedir($dh); // If this is set it means we timed out half way through. if ($dont_chmod) { $context['total_files'] = $file_count; pausePermsSave(); } // Do the actual directory. $status = $context['predefined_type'] === 'free' || isset($context['special_files'][$path]) ? 'writable' : 'execute'; package_chmod($path, $status); // We've finished the directory so no file offset, and no record. $context['file_offset'] = 0; unset($context['directory_list'][$path]); // See if we're out of time? if (time() - array_sum(explode(' ', $time_start)) > $timeout_limit) { pausePermsSave(); } } } // If we're here we are done! redirectexit('action=admin;area=packages;sa=perms' . (!empty($context['back_look_data']) ? ';back_look=' . base64_encode(serialize($context['back_look_data'])) : '') . ';' . $context['session_var'] . '=' . $context['session_id']); }
function package_create_backup($id = 'backup') { global $sourcedir, $boarddir, $smcFunc; $files = array(); $base_files = array('index.php', 'SSI.php', 'agreement.txt', 'ssi_examples.php', 'ssi_examples.shtml'); foreach ($base_files as $file) { if (file_exists($boarddir . '/' . $file)) { $files[realpath($boarddir . '/' . $file)] = array(empty($_REQUEST['use_full_paths']) ? $file : $boarddir . '/' . $file, stat($boarddir . '/' . $file)); } } $dirs = array($sourcedir => empty($_REQUEST['use_full_paths']) ? 'Sources/' : strtr($sourcedir . '/', '\\', '/')); $request = smf_db_query(' SELECT value FROM {db_prefix}themes WHERE id_member = {int:no_member} AND variable = {string:theme_dir}', array('no_member' => 0, 'theme_dir' => 'theme_dir')); while ($row = mysql_fetch_assoc($request)) { $dirs[$row['value']] = empty($_REQUEST['use_full_paths']) ? 'Themes/' . basename($row['value']) . '/' : strtr($row['value'] . '/', '\\', '/'); } mysql_free_result($request); while (!empty($dirs)) { list($dir, $dest) = each($dirs); unset($dirs[$dir]); $listing = @dir($dir); if (!$listing) { continue; } while ($entry = $listing->read()) { if (preg_match('~^(\\.{1,2}|CVS|backup.*|help|images|.*\\~)$~', $entry) != 0) { continue; } $filepath = realpath($dir . '/' . $entry); if (isset($files[$filepath])) { continue; } $stat = stat($dir . '/' . $entry); if ($stat['mode'] & 040000) { $files[$filepath] = array($dest . $entry . '/', $stat); $dirs[$dir . '/' . $entry] = $dest . $entry . '/'; } else { $files[$filepath] = array($dest . $entry, $stat); } } $listing->close(); } if (!file_exists($boarddir . '/Packages/backups')) { mktree($boarddir . '/Packages/backups', 0777); } if (!is_writable($boarddir . '/Packages/backups')) { package_chmod($boarddir . '/Packages/backups'); } $output_file = $boarddir . '/Packages/backups/' . strftime('%Y-%m-%d_') . preg_replace('~[$\\\\/:<>|?*"\']~', '', $id); $output_ext = '.tar' . (function_exists('gzopen') ? '.gz' : ''); if (file_exists($output_file . $output_ext)) { $i = 2; while (file_exists($output_file . '_' . $i . $output_ext)) { $i++; } $output_file = $output_file . '_' . $i . $output_ext; } else { $output_file .= $output_ext; } @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } if (function_exists('gzopen')) { $fwrite = 'gzwrite'; $fclose = 'gzclose'; $output = gzopen($output_file, 'wb'); } else { $fwrite = 'fwrite'; $fclose = 'fclose'; $output = fopen($output_file, 'wb'); } foreach ($files as $real_file => $file) { if (!file_exists($real_file)) { continue; } $stat = $file[1]; if (substr($file[0], -1) == '/') { $stat['size'] = 0; } $current = pack('a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12', $file[0], decoct($stat['mode']), sprintf('%06d', decoct($stat['uid'])), sprintf('%06d', decoct($stat['gid'])), decoct($stat['size']), decoct($stat['mtime']), '', 0, '', '', '', '', '', '', '', '', ''); $checksum = 256; for ($i = 0; $i < 512; $i++) { $checksum += ord($current[$i]); } $fwrite($output, substr($current, 0, 148) . pack('a8', decoct($checksum)) . substr($current, 156, 511)); if ($stat['size'] == 0) { continue; } $fp = fopen($real_file, 'rb'); while (!feof($fp)) { $fwrite($output, fread($fp, 16384)); } fclose($fp); $fwrite($output, pack('a' . (512 - $stat['size'] % 512), '')); } $fwrite($output, pack('a1024', '')); $fclose($output); }
/** * Creates a site backup before installing a package just in case things don't go * as planned. * * @package Packages * @param string $id */ function package_create_backup($id = 'backup') { $db = database(); $files = array(); // The files that reside outside of sources, in the base, we add manually $base_files = array('index.php', 'SSI.php', 'agreement.txt', 'ssi_examples.php', 'ssi_examples.shtml', 'subscriptions.php', 'email_imap_cron.php', 'emailpost.php', 'emailtopic.php'); foreach ($base_files as $file) { if (file_exists(BOARDDIR . '/' . $file)) { $files[realpath(BOARDDIR . '/' . $file)] = array(empty($_REQUEST['use_full_paths']) ? $file : BOARDDIR . '/' . $file, stat(BOARDDIR . '/' . $file)); } } // Root directory where most of our files reside $dirs = array(SOURCEDIR => empty($_REQUEST['use_full_paths']) ? 'sources/' : strtr(SOURCEDIR . '/', '\\', '/')); // Find all installed theme directories $request = $db->query('', ' SELECT value FROM {db_prefix}themes WHERE id_member = {int:no_member} AND variable = {string:theme_dir}', array('no_member' => 0, 'theme_dir' => 'theme_dir')); while ($row = $db->fetch_assoc($request)) { $dirs[$row['value']] = empty($_REQUEST['use_full_paths']) ? 'themes/' . basename($row['value']) . '/' : strtr($row['value'] . '/', '\\', '/'); } $db->free_result($request); // While we have directorys to check while (!empty($dirs)) { list($dir, $dest) = each($dirs); unset($dirs[$dir]); // Get the file listing for this directory $listing = @dir($dir); if (!$listing) { continue; } while ($entry = $listing->read()) { if (preg_match('~^(\\.{1,2}|CVS|backup.*|help|images|.*\\~)$~', $entry) != 0) { continue; } $filepath = realpath($dir . '/' . $entry); if (isset($files[$filepath])) { continue; } $stat = stat($dir . '/' . $entry); // If this is a directory, add it to the dir stack for processing if ($stat['mode'] & 040000) { $files[$filepath] = array($dest . $entry . '/', $stat); $dirs[$dir . '/' . $entry] = $dest . $entry . '/'; } else { $files[$filepath] = array($dest . $entry, $stat); } } $listing->close(); } // Make sure we have a backup directory and its writable if (!file_exists(BOARDDIR . '/packages/backups')) { mktree(BOARDDIR . '/packages/backups', 0777); } if (!is_writable(BOARDDIR . '/packages/backups')) { package_chmod(BOARDDIR . '/packages/backups'); } // Name the output file, yyyy-mm-dd_before_package_name.tar.gz $output_file = BOARDDIR . '/packages/backups/' . strftime('%Y-%m-%d_') . preg_replace('~[$\\\\/:<>|?*"\']~', '', $id); $output_ext = '.tar' . (function_exists('gzopen') ? '.gz' : ''); if (file_exists($output_file . $output_ext)) { $i = 2; while (file_exists($output_file . '_' . $i . $output_ext)) { $i++; } $output_file = $output_file . '_' . $i . $output_ext; } else { $output_file .= $output_ext; } // Buy some more time so we have enough to create this archive @set_time_limit(300); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Set up the file output handle, try gzip first to save space if (function_exists('gzopen')) { $fwrite = 'gzwrite'; $fclose = 'gzclose'; $output = gzopen($output_file, 'wb'); } else { $fwrite = 'fwrite'; $fclose = 'fclose'; $output = fopen($output_file, 'wb'); } // For each file we found in the directory, we add them to a TAR archive foreach ($files as $real_file => $file) { if (!file_exists($real_file)) { continue; } // Check if its a directory $stat = $file[1]; if (substr($file[0], -1) == '/') { $stat['size'] = 0; } // Create a tar file header, pack the details in to the fields $current = pack('a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12', $file[0], decoct($stat['mode']), sprintf('%06d', decoct($stat['uid'])), sprintf('%06d', decoct($stat['gid'])), decoct($stat['size']), decoct($stat['mtime']), '', 0, '', '', '', '', '', '', '', '', ''); // Create the header checksum $checksum = 256; for ($i = 0; $i < 512; $i++) { $checksum += ord($current[$i]); } // Write out the file header (insert the checksum we just computed) $fwrite($output, substr($current, 0, 148) . pack('a8', decoct($checksum)) . substr($current, 156, 511)); // If this is a directory entry all thats needed is the header if ($stat['size'] == 0) { continue; } // Write the actual file contents to the backup file $fp = fopen($real_file, 'rb'); while (!feof($fp)) { $fwrite($output, fread($fp, 16384)); } fclose($fp); // Pad the output so its on 512 boundarys $fwrite($output, pack('a' . (512 - $stat['size'] % 512), '')); } $fwrite($output, pack('a1024', '')); $fclose($output); }