public function onBrowse($tpl = null) { AkeebaStrapper::addJSfile('media://com_akeeba/js/fsfilter.js'); $model = $this->getModel(); $task = $model->getState('browse_task', 'normal'); // Add custom submenus $toolbar = F0FToolbar::getAnInstance($this->input->get('option', 'com_foobar', 'cmd'), $this->config); $toolbar->appendLink(JText::_('FILTERS_LABEL_NORMALVIEW'), JUri::base() . 'index.php?option=com_akeeba&view=fsfilter&task=normal', $task == 'normal'); $toolbar->appendLink(JText::_('FILTERS_LABEL_TABULARVIEW'), JUri::base() . 'index.php?option=com_akeeba&view=fsfilter&task=tabular', $task == 'tabular'); $media_folder = JUri::base() . '../media/com_akeeba/'; // Get the root URI for media files $this->mediadir = AkeebaHelperEscape::escapeJS($media_folder . 'theme/'); // Get a JSON representation of the available roots $filters = Factory::getFilters(); $root_info = $filters->getInclusions('dir'); $roots = array(); $options = array(); if (!empty($root_info)) { // Loop all dir definitions foreach ($root_info as $dir_definition) { if (is_null($dir_definition[1])) { // Site root definition has a null element 1. It is always pushed on top of the stack. array_unshift($roots, $dir_definition[0]); } else { $roots[] = $dir_definition[0]; } $options[] = JHTML::_('select.option', $dir_definition[0], $dir_definition[0]); } } $site_root = $roots[0]; $attribs = 'onchange="akeeba.Fsfilters.activeRootChanged();"'; $this->root_select = JHTML::_('select.genericlist', $options, 'root', $attribs, 'value', 'text', $site_root, 'active_root'); $this->roots = $roots; switch ($task) { case 'normal': default: $this->setLayout('default'); // Get a JSON representation of the directory data $model = $this->getModel(); $json = json_encode($model->make_listing($site_root, array(), '')); $this->json = $json; break; case 'tabular': $this->setLayout('tabular'); // Get a JSON representation of the tabular filter data $model = $this->getModel(); $json = json_encode($model->get_filters($site_root)); $this->json = $json; break; } // Get profile ID $profileid = Platform::getInstance()->get_active_profile(); $this->profileid = $profileid; // Get profile name $pmodel = F0FModel::getAnInstance('Profiles', 'AkeebaModel'); $pmodel->setId($profileid); $profile_data = $pmodel->getItem(); $this->profilename = $this->escape($profile_data->description); return true; }
/** * Returns an array containing a mapping of db root names and their human-readable representation * * @return array Array of objects; "value" contains the root name, "text" the human-readable text */ public function get_roots() { // Get database inclusion filters $filters = Factory::getFilters(); $database_list = $filters->getInclusions('db'); $ret = array(); foreach ($database_list as $name => $definition) { $root = $definition['host']; if (!empty($definition['port'])) { $root .= ':' . $definition['port']; } $root .= '/' . $definition['database']; if ($name == '[SITEDB]') { $root = JText::_('COM_AKEEBA_DBFILTER_LABEL_SITEDB'); } $ret[] = (object) ['value' => $name, 'text' => $root]; } return $ret; }
/** * Steps the files scanning of the current directory * * @return boolean True on success, false on fatal error */ protected function scanFiles() { $engine = Factory::getScanEngine(); list($root, $translated_root, $dir) = $this->getCleanDirectoryComponents(); // Get a filters instance $filters = Factory::getFilters(); if (is_null($this->getFiles_position)) { Factory::getLog()->log(LogLevel::INFO, "Scanning files of " . $this->current_directory); $this->processed_files_counter = 0; } else { Factory::getLog()->log(LogLevel::INFO, "Resuming scanning files of " . $this->current_directory); } // Get file listing $fileList = $engine->getFiles($this->current_directory, $this->getFiles_position); // Error propagation $this->propagateFromObject($engine); // If the list contains "too many" items, please break this step! if (Factory::getConfiguration()->get('volatile.breakflag', false)) { // Log the step break decision, for debugging reasons Factory::getLog()->log(LogLevel::INFO, "Large directory " . $this->current_directory . " while scanning for files; I will resume scanning in next step."); // Return immediately, marking that we are not done yet! return true; } // Error control if ($this->getError()) { return false; } // Do I have an unreadable directory? if ($fileList === false) { $this->setWarning('Unreadable directory ' . $this->current_directory); $this->done_file_scanning = true; } else { if (is_array($fileList) && !empty($fileList)) { // Add required trailing slash to $dir if (!empty($dir)) { $dir .= '/'; } // Scan all directory entries foreach ($fileList as $fileName) { $check = $dir . basename($fileName); if (_AKEEBA_IS_WINDOWS) { $check = Factory::getFilesystemTools()->TranslateWinPath($check); } // Do I need this? $dir contains a path relative to the root anyway... $check = ltrim(str_replace($translated_root, '', $check), '/'); $byFilter = ''; $skipThisFile = $filters->isFilteredExtended($check, $root, 'file', 'all', $byFilter); if ($skipThisFile) { Factory::getLog()->log(LogLevel::INFO, "Skipping file {$fileName} (filter: {$byFilter})"); } else { $this->file_list[] = $fileName; $this->processed_files_counter++; $this->progressAddFile(); } } } } // If the scanner engine nullified the next position we are done // scanning for files if (is_null($this->getFiles_position)) { $this->done_file_scanning = true; } // If the directory was genuinely empty we will have to add an empty // directory entry in the archive, otherwise this directory will never // be restored. if ($this->done_file_scanning && $this->processed_files_counter == 0) { Factory::getLog()->log(LogLevel::INFO, "Empty directory " . $this->current_directory); $archiver = Factory::getArchiverEngine(); if ($this->current_directory != $this->remove_path_prefix) { $archiver->addFile($this->current_directory, $this->remove_path_prefix, $this->path_prefix); } // Error propagation $this->propagateFromObject($archiver); // Check for errors if ($this->getError()) { return false; } unset($archiver); } return true; }
/** * Resets the filters * @param string $root Root directory * @return array */ public function resetFilters($root) { // Get a reference to the global Filters object $filters = Factory::getFilters(); $filter = Factory::getFilterObject('directories'); $filter->reset($root); $filter = Factory::getFilterObject('files'); $filter->reset($root); $filter = Factory::getFilterObject('skipdirs'); $filter->reset($root); $filter = Factory::getFilterObject('skipfiles'); $filter->reset($root); $filters->save(); return $this->make_listing($root); }
/** * Populates the _tables array with the metadata of each table * * @return void Updates $this->tables_data and $this->tables */ protected function get_tables_data_without_dependencies() { Factory::getLog()->log(LogLevel::DEBUG, __CLASS__ . " :: Pushing table data (without dependency tracking)"); // Reset internal tables $this->tables_data = array(); $this->dependencies = array(); // Get filters and filter root $registry = Factory::getConfiguration(); $root = $registry->get('volatile.database.root', '[SITEDB]'); $filters = Factory::getFilters(); foreach ($this->table_name_map as $table_name => $table_abstract) { $new_entry = array('type' => 'table', 'dump_records' => true); // Table Data Filter - skip dumping table contents of filtered out tables if ($filters->isFiltered($table_abstract, $root, 'dbobject', 'content')) { $new_entry['dump_records'] = false; } $this->tables_data[$table_name] = $new_entry; $this->tables[] = $table_name; } // foreach Factory::getLog()->log(LogLevel::DEBUG, __CLASS__ . " :: Got table list"); }
/** * Populates database_list with the list of databases in the settings * * @return void */ protected function populate_database_list() { // Get database inclusion filters $filters = Factory::getFilters(); $this->database_list = $filters->getInclusions('db'); // Error propagation $this->propagateFromObject($filters); if ($this->getError()) { return; } if (Factory::getEngineParamsProvider()->getScriptingParameter('db.skipextradb', 0)) { // On database only backups we prune extra databases Factory::getLog()->log(LogLevel::DEBUG, __CLASS__ . " :: Adding only main database"); if (count($this->database_list) > 1) { $this->database_list = array_slice($this->database_list, 0, 1); } } }
/** * Resets the filters * * @param string $root Root directory * * @return array */ protected function resetAllFilters($root) { // Get a reference to the global Filters object $filters = Factory::getFilters(); foreach ($this->knownFilterTypes as $filterName) { $filter = Factory::getFilterObject($filterName); $filter->reset($root); } $filters->save(); }
/** * Returns a listing of contained directories and files, as well as their exclusion status * * @param string $root The root directory * @param string $node The subdirectory to scan * * @return array */ private function &get_listing($root, $node) { // Initialize the absolute directory root $directory = substr($root, 0); // Replace stock directory tags, like [SITEROOT] $stock_dirs = Platform::getInstance()->get_stock_directories(); if (!empty($stock_dirs)) { foreach ($stock_dirs as $key => $replacement) { $directory = str_replace($key, $replacement, $directory); } } $directory = Factory::getFilesystemTools()->TranslateWinPath($directory); // Clean and add the node $node = Factory::getFilesystemTools()->TranslateWinPath($node); // Just a directory separator is treated as no directory at all if ($node == '/') { $node = ''; } // Trim leading and trailing slashes $node = trim($node, '/'); // Add node to directory if (!empty($node)) { $directory .= '/' . $node; } // Add any required trailing slash to the node to be used below if (!empty($node)) { $node .= '/'; } // Get a filters instance $filters = Factory::getFilters(); // Get a listing of folders and process it $folders = Factory::getFileLister()->getFolders($directory); $folders_out = array(); if (!empty($folders)) { asort($folders); foreach ($folders as $folder) { $folder = Factory::getFilesystemTools()->TranslateWinPath($folder); // Filter out files whose names result to an empty JSON representation $json_folder = json_encode($folder); $folder = json_decode($json_folder); if (empty($folder)) { continue; } $test = $node . $folder; $status = array(); // Check dir/all filter (exclude) $result = $filters->isFilteredExtended($test, $root, 'dir', 'all', $byFilter); $status['directories'] = !$result ? 0 : ($byFilter == 'directories' ? 1 : 2); // Check dir/content filter (skip_files) $result = $filters->isFilteredExtended($test, $root, 'dir', 'content', $byFilter); $status['skipfiles'] = !$result ? 0 : ($byFilter == 'skipfiles' ? 1 : 2); // Check dir/children filter (skip_dirs) $result = $filters->isFilteredExtended($test, $root, 'dir', 'children', $byFilter); $status['skipdirs'] = !$result ? 0 : ($byFilter == 'skipdirs' ? 1 : 2); // Add to output array $folders_out[$folder] = $status; } } unset($folders); $folders = $folders_out; // Get a listing of files and process it $files = Factory::getFileLister()->getFiles($directory); $files_out = array(); if (!empty($files)) { asort($files); foreach ($files as $file) { // Filter out files whose names result to an empty JSON representation $json_file = json_encode($file); $file = json_decode($json_file); if (empty($file)) { continue; } $test = $node . $file; $status = array(); // Check file/all filter (exclude) $result = $filters->isFilteredExtended($test, $root, 'file', 'all', $byFilter); $status['files'] = !$result ? 0 : ($byFilter == 'files' ? 1 : 2); $status['size'] = @filesize($directory . '/' . $file); // Add to output array $files_out[$file] = $status; } } unset($files); $files = $files_out; // Return a compiled array $retarray = array('folders' => $folders, 'files' => $files); return $retarray; /* Return array format * [array] : * 'folders' [array] : * (folder_name) => [array]: * 'directories' => 0|1|2 * 'skipfiles' => 0|1|2 * 'skipdirs' => 0|1|2 * 'files' [array] : * (file_name) => [array]: * 'files' => 0|1|2 * * Legend: * 0 -> Not excluded * 1 -> Excluded by the direct filter * 2 -> Excluded by another filter (regex, api, an unknown plugin filter...) */ }
/** * Reverse engineers the View definitions of this database * * @param DriverBase $dbi Database connection to INFORMATION_SCHEMA */ protected function reverse_engineer_views(&$dbi) { $schema_name = $this->database; $sql = 'SELECT * FROM [INFORMATION_SCHEMA].[VIEWS] WHERE [table_catalog] = ' . $dbi->quote($schema_name); $dbi->setQuery($sql); $all_views = $dbi->loadObjectList(); $registry = Factory::getConfiguration(); $root = $registry->get('volatile.database.root', '[SITEDB]'); // If we have filters, make sure the tables pass the filtering $filters = Factory::getFilters(); // First pass: populate the table_name_map if (!empty($all_views)) { foreach ($all_views as $table_object) { // Extract the table name $table_name = $table_object->TABLE_NAME; // Filter and convert if (substr($table_name, 0, 3) == '#__') { $warningMessage = __CLASS__ . " :: Table {$table_name} has a prefix of #__. This would cause restoration errors; table skipped."; $this->setWarning($warningMessage); Factory::getLog()->log(LogLevel::WARNING, $warningMessage); continue; } $table_abstract = $this->getAbstract($table_name); if (substr($table_abstract, 0, 4) != 'bak_') { // Apply exclusion filters if (!$filters->isFiltered($table_abstract, $root, 'dbobject', 'all')) { Factory::getLog()->log(LogLevel::INFO, __CLASS__ . " :: Adding {$table_name} (internal name {$table_abstract})"); $this->table_name_map[$table_name] = $table_abstract; } else { Factory::getLog()->log(LogLevel::INFO, __CLASS__ . " :: Skipping {$table_name} (internal name {$table_abstract})"); continue; } } else { Factory::getLog()->log(LogLevel::INFO, __CLASS__ . " :: Backup view {$table_name} automatically skipped."); continue; } } } // Second pass: get the create commands if (!empty($all_views)) { foreach ($all_views as $table_object) { // Extract the table name $table_name = $table_object->TABLE_NAME; if (!in_array($table_name, $this->table_name_map)) { // Skip any views which have been filtered out continue; } $table_abstract = $this->getAbstract($table_name); // Still here? The view is added. We now have to store its // create command, dependency info and so on $new_entry = array('type' => 'view', 'dump_records' => false); $dependencies = array(); $table_sql = $table_object->VIEW_DEFINITION; $old_table_sql = $table_sql; foreach ($this->table_name_map as $ref_normal => $ref_abstract) { if ($pos = strpos($table_sql, ".{$ref_normal}")) { // Add a reference hit $this->dependencies[$ref_normal][] = $table_name; // Add the dependency to this table's metadata $dependencies[] = $ref_normal; // Do the replacement $table_sql = str_replace(".{$ref_normal}", ".{$ref_abstract}", $table_sql); } } // On DB only backup we don't want any replacing to take place, do we? if (!Factory::getEngineParamsProvider()->getScriptingParameter('db.abstractnames', 1)) { $table_sql = $old_table_sql; } // Replace newlines with spaces $table_sql = str_replace("\n", " ", $table_sql) . ";\n"; $table_sql = str_replace("\r", " ", $table_sql); $table_sql = str_replace("\t", " ", $table_sql); $new_entry['create'] = $table_sql; $new_entry['dependencies'] = $dependencies; $this->tables_data[$table_name] = $new_entry; } } }
/** * Implements the _finalize() abstract method * */ protected function _finalize() { Factory::getLog()->log(LogLevel::DEBUG, "Adding any extra SQL statements imposed by the filters"); $filters = Factory::getFilters(); $this->writeline($filters->getExtraSQL($this->databaseRoot)); // Close the file pointer (otherwise the SQL file is left behind) $this->closeFile(); // If we are not just doing a main db only backup, add the SQL file to the archive $finished = true; $configuration = Factory::getConfiguration(); if (Factory::getEngineParamsProvider()->getScriptingParameter('db.saveasname', 'normal') != 'output') { $archiver = Factory::getArchiverEngine(); $configuration = Factory::getConfiguration(); if ($configuration->get('volatile.engine.archiver.processingfile', false)) { // We had already started archiving the db file, but it needs more time Factory::getLog()->log(LogLevel::DEBUG, "Continuing adding the SQL dump to the archive"); $archiver->addFile(null, null, null); $this->propagateFromObject($archiver); if ($this->getError()) { return; } $finished = !$configuration->get('volatile.engine.archiver.processingfile', false); } else { // We have to add the dump file to the archive Factory::getLog()->log(LogLevel::DEBUG, "Adding the final SQL dump to the archive"); $archiver->addFileRenamed($this->tempFile, $this->saveAsName); $this->propagateFromObject($archiver); if ($this->getError()) { return; } $finished = !$configuration->get('volatile.engine.archiver.processingfile', false); } } else { // We just have to move the dump file to its final destination Factory::getLog()->log(LogLevel::DEBUG, "Moving the SQL dump to its final location"); $result = Platform::getInstance()->move($this->tempFile, $this->saveAsName); if (!$result) { $this->setError('Could not move the SQL dump to its final location'); } } // Make sure that if the archiver needs more time to process the file we can supply it if ($finished) { Factory::getLog()->log(LogLevel::DEBUG, "Removing temporary file of final SQL dump"); Factory::getTempFiles()->unregisterAndDeleteTempFile($this->tempFile, true); if ($this->getError()) { return; } $this->setState('finished'); } }
/** * Returns a list of filter strings for the given root. Used by MySQLDump engine. * * @param string $root * * @return array */ public function getFilters($root) { $dummy = array(); if (!$this->enabled) { return $dummy; } switch ($this->method) { default: case 'api': // API filters never have a list return $dummy; break; case 'direct': case 'regex': // Get a local reference of the filter data, if necessary if (is_null($this->filter_data)) { $filters = Factory::getFilters(); $this->filter_data = $filters->getFilterData($this->filter_name); } if (is_null($root)) { // When NULL is passed as the root, we return all roots return $this->filter_data; } elseif (array_key_exists($root, $this->filter_data)) { // The root exists, return its data return $this->filter_data[$root]; } else { // The root doesn't exist, return an empty array return $dummy; } break; } }
/** * @return void */ public function onBeforeMain() { $this->addJavascriptFile('media://com_akeeba/js/FileFilters.min.js'); /** @var FileFilters $model */ $model = $this->getModel(); // Add custom submenus $task = $model->getState('browse_task', 'normal'); $toolbar = $this->container->toolbar; $toolbar->appendLink(JText::_('COM_AKEEBA_FILEFILTERS_LABEL_NORMALVIEW'), JUri::base() . 'index.php?option=com_akeeba&view=FileFilters&task=normal', $task == 'normal'); $toolbar->appendLink(JText::_('COM_AKEEBA_FILEFILTERS_LABEL_TABULARVIEW'), JUri::base() . 'index.php?option=com_akeeba&view=FileFilters&task=tabular', $task == 'tabular'); // Get a JSON representation of the available roots $filters = Factory::getFilters(); $root_info = $filters->getInclusions('dir'); $roots = array(); $options = array(); if (!empty($root_info)) { // Loop all dir definitions foreach ($root_info as $dir_definition) { if (is_null($dir_definition[1])) { // Site root definition has a null element 1. It is always pushed on top of the stack. array_unshift($roots, $dir_definition[0]); } else { $roots[] = $dir_definition[0]; } $options[] = JHtml::_('select.option', $dir_definition[0], $dir_definition[0]); } } $site_root = $roots[0]; $attribs = 'onchange="akeeba.Fsfilters.activeRootChanged();"'; $this->root_select = JHtml::_('select.genericlist', $options, 'root', $attribs, 'value', 'text', $site_root, 'active_root'); $this->roots = $roots; switch ($task) { case 'normal': default: $this->setLayout('default'); // Get a JSON representation of the directory data $model = $this->getModel(); $json = json_encode($model->make_listing($site_root, array(), '')); $this->json = $json; break; case 'tabular': $this->setLayout('tabular'); // Get a JSON representation of the tabular filter data $model = $this->getModel(); $json = json_encode($model->get_filters($site_root)); $this->json = $json; break; } // Push translations JText::script('COM_AKEEBA_FILEFILTERS_LABEL_UIROOT'); JText::script('COM_AKEEBA_FILEFILTERS_LABEL_UIERRORFILTER'); JText::script('COM_AKEEBA_FILEFILTERS_TYPE_DIRECTORIES'); JText::script('COM_AKEEBA_FILEFILTERS_TYPE_SKIPFILES'); JText::script('COM_AKEEBA_FILEFILTERS_TYPE_SKIPDIRS'); JText::script('COM_AKEEBA_FILEFILTERS_TYPE_FILES'); JText::script('COM_AKEEBA_FILEFILTERS_TYPE_DIRECTORIES_ALL'); JText::script('COM_AKEEBA_FILEFILTERS_TYPE_SKIPFILES_ALL'); JText::script('COM_AKEEBA_FILEFILTERS_TYPE_SKIPDIRS_ALL'); JText::script('COM_AKEEBA_FILEFILTERS_TYPE_FILES_ALL'); JText::script('COM_AKEEBA_FILEFILTERS_TYPE_APPLYTOALLDIRS'); JText::script('COM_AKEEBA_FILEFILTERS_TYPE_APPLYTOALLFILES'); $this->getProfileIdAndName(); }
/** * Q106 - HIGH - Table name prefix contains uppercase characters * * @return bool */ private function q106() { $filters = Factory::getFilters(); $databases = $filters->getInclusions('db'); foreach ($databases as $db) { if (!isset($db['prefix'])) { continue; } if (preg_match('/[A-Z]/', $db['prefix'])) { return true; } } return false; }