You are here

class backup_migrate_destination_files in Backup and Migrate 6.2

Same name and namespace in other branches
  1. 8.2 includes/destinations.file.inc \backup_migrate_destination_files
  2. 8.3 includes/destinations.file.inc \backup_migrate_destination_files
  3. 6.3 includes/destinations.file.inc \backup_migrate_destination_files
  4. 7.3 includes/destinations.file.inc \backup_migrate_destination_files
  5. 7.2 includes/destinations.file.inc \backup_migrate_destination_files

A destination type for saving locally to the server.

Hierarchy

Expanded class hierarchy of backup_migrate_destination_files

1 string reference to 'backup_migrate_destination_files'
backup_migrate_backup_migrate_destination_types in includes/destinations.inc
Implementation of hook_backup_migrate_destination_types().

File

includes/destinations.file.inc, line 15
A destination type for saving locally to the server.

View source
class backup_migrate_destination_files extends backup_migrate_destination {
  var $supported_ops = array(
    'scheduled backup',
    'manual backup',
    'restore',
    'list files',
    'configure',
    'delete',
  );
  function type_name() {
    return t("Server Directory");
  }

  /**
   * File save destination callback.
   */
  function save_file($file, $settings) {
    if ($dir = $this
      ->get_location()) {
      if ($this
        ->check_dir($dir)) {
        $filepath = rtrim($dir, "/") . "/" . $file
          ->filename();
        rename($file
          ->filepath(), $filepath);

        // chmod, chown and chgrp the file if needed.
        if ($chmod = $this
          ->settings('chmod')) {
          if (!@chmod($filepath, octdec($chmod))) {
            _backup_migrate_message('Unable to set the file mode for: @file', array(
              '@file' => $filepath,
            ), 'error');
          }
        }
        if ($chgrp = $this
          ->settings('chgrp')) {
          if (!@chgrp($filepath, $chgrp)) {
            _backup_migrate_message('Unable to set the file group for: @file', array(
              '@file' => $filepath,
            ), 'error');
          }
        }
        return $file;
      }
    }
  }

  /**
   * Determine if we can read the given file.
   */
  function can_read_file($file_id) {
    return $this
      ->op('restore') && is_readable($this
      ->get_filepath($file_id));
  }

  /**
   * File load destination callback.
   */
  function load_file($file_id) {
    $filepath = $this
      ->get_filepath($file_id);
    if (file_exists($filepath)) {
      backup_migrate_include('files');
      return new backup_file(array(
        'filepath' => $filepath,
      ));
    }
  }

  /**
   * Get the file object for the given file.
   */
  function get_file($file_id) {
    $files = $this
      ->list_files();
    if (isset($files[$file_id])) {
      isset($files[$file_id]);
    }
    return NULL;
  }

  /**
   * File list destination callback.
   */
  function list_files() {
    $files = array();
    if ($dir = $this
      ->get_location()) {
      if ($handle = @opendir($dir)) {
        backup_migrate_include('files');
        while (FALSE !== ($file = readdir($handle))) {
          $filepath = $dir . "/" . $file;
          $files[$file] = new backup_file(array(
            'filepath' => $filepath,
          ));
        }
      }
    }
    return $files;
  }

  /**
   * File delete destination callback.
   */
  function delete_file($file_id) {
    $filepath = $this
      ->get_filepath($file_id);
    file_delete($filepath);
  }

  /**
   * Get the filepath from the given file id.
   */
  function get_filepath($file_id) {
    $filepath = rtrim($this
      ->get_location(), '/') . '/' . $file_id;
    return $filepath;
  }

  /**
   * Get the form for the settings for the files destination.
   */
  function edit_form() {
    $form = parent::edit_form();
    $form['location'] = array(
      "#type" => "textfield",
      "#title" => t("Directory path"),
      "#default_value" => $this
        ->get_location(),
      "#required" => TRUE,
      "#description" => t('Enter the path to the directory to save the backups to. Use a relative path to pick a path relative to your Drupal root directory. The web server must be able to write to this path.'),
    );
    $form['settings'] = array(
      '#type' => 'fieldset',
      '#title' => t('Advanced Settings'),
      '#tree' => TRUE,
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    if (function_exists('chmod')) {
      $form['settings']['chmod'] = array(
        '#type' => 'textfield',
        '#title' => t('Change file mode (chmod)'),
        '#size' => 5,
        '#default_value' => $this
          ->settings('chmod'),
        '#description' => t('If you enter a value here, backup files will be chmoded with the mode you specify. Specify the mode in octal form (e.g. 644 or 0644) or leave blank to disable this feature.'),
      );
    }
    if (function_exists('chgrp')) {
      $form['settings']['chgrp'] = array(
        '#type' => 'textfield',
        '#title' => t('Change file group (chgrp)'),
        '#size' => 5,
        '#default_value' => $this
          ->settings('chgrp'),
        '#description' => t('If you enter a value here, backup files will be chgrped to the group you specify. Leave blank to disable this feature.'),
      );
    }
    return $form;
  }

  /**
   * Validate the form for the settings for the files destination.
   */
  function edit_form_validate($form, &$form_state) {
    $values = $form_state['values'];
    if (isset($values['settings']['chmod']) && !empty($values['settings']['chmod']) && !preg_match('/0?[0-7]{3}/', $values['settings']['chmod'])) {
      form_set_error('chmod', t('You must enter a valid chmod octal value (e.g. 644 or 0644) in the change mode field, or leave it blank.'));
    }
    parent::edit_form_validate($form, $form_state);
  }

  /**
   * Submit the form for the settings for the files destination.
   */
  function edit_form_submit($form, &$form_state) {

    // Add a 0 to the start of a 3 digit file mode to make it proper PHP encoded octal.
    if (strlen($form_state['values']['settings']['chmod']) == 3) {
      $form_state['values']['settings']['chmod'] = '0' . $form_state['values']['settings']['chmod'];
    }
    parent::edit_form_submit($form, $form_state);
  }

  /**
   * Prepare the destination directory for the backups.
   */
  function check_dir($directory) {
    $out = TRUE;
    $dirs = array();

    // If the directory doesn't exist try to create it.
    if (!is_dir($directory)) {
      foreach (explode('/', $directory) as $dir) {
        $dirs[] = $dir;
        $path = implode($dirs, '/');
        if ($path && !is_dir(realpath($path)) && !file_check_directory($path, FILE_CREATE_DIRECTORY)) {
          $out = FALSE;
        }
      }
      if (!$out || !file_check_directory($directory)) {

        // Unable to create destination directory.
        _backup_migrate_message("Unable to create or write to the save directory '%directory'. Please check the file permissions that directory and try again.", array(
          '%directory' => $directory,
        ), "error");
        return FALSE;
      }
    }

    // If the destination directory is within the webroot, then secure it as best we can.
    if ($this
      ->dir_in_webroot($directory)) {
      $directory = $this
        ->check_web_dir($directory);
    }
    return $directory;
  }

  /**
   * Check that a web accessible directory has been properly secured, othewise attempt to secure it.
   */
  function check_web_dir($directory) {

    // If the directory is specified with an absolute path, strip the site root.
    $directory = substr(realpath($directory), strlen(realpath('.') . '/'));

    // Check for a htaccess file which adequately protects the backup files.
    $htaccess_lines = "order allow,deny\ndeny from all\n";
    if (!is_file($directory . '/.htaccess') || !is_readable($directory . '/.htaccess') || strpos(file_get_contents($directory . '/.htaccess'), $htaccess_lines) === FALSE) {

      // Attempt to protect the backup files from public access using htaccess.
      if (($fp = @fopen($directory . '/.htaccess', 'w')) && @fputs($fp, $htaccess_lines)) {
        fclose($fp);
        chmod($directory . '/.htaccess', 0664);
      }
      else {
        $message = "Security warning: Couldn't modify .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: <code>!htaccess</code> or add them to the existing .htaccess file";
        $replace = array(
          '%directory' => $directory,
          '!htaccess' => '<br />' . nl2br(check_plain($htaccess_lines)),
        );
        _backup_migrate_message($message, $replace, 'error');
        return FALSE;
      }
    }

    // Check the user agent to make sure we're not responding to a request from drupal itself.
    // That should prevent infinite loops which could be caused by poormanscron in some circumstances.
    if (strpos($_SERVER['HTTP_USER_AGENT'], 'Drupal') !== FALSE) {
      return FALSE;
    }

    // Check to see if the destination is publicly accessible
    $test_contents = "this file should not be publicly accessible";

    // Create the the text.txt file if it's not already there.
    if (!is_file($directory . '/test.txt') || file_get_contents($directory . '/test.txt') != $test_contents) {
      if ($fp = fopen($directory . '/test.txt', 'w')) {
        @fputs($fp, $test_contents);
        fclose($fp);
      }
      else {
        $message = t("Security notice: Backup and Migrate was unable to write a test text file to the destination directory %directory, and is therefore unable to check the security of the backup destination. Backups to the server will be disabled until the destination becomes writable and secure.", array(
          '%directory' => $directory,
        ));
        _backup_migrate_message($message, array(), "error");
        return FALSE;
      }
    }

    // Attempt to read the test file via http. This may fail for other reasons,
    // so it's not a bullet-proof check.
    if ($this
      ->test_file_readable_remotely($directory . '/test.txt', $test_contents)) {
      $message = t("Security notice: Backup and Migrate will not save backup files to the server because the destination directory is publicly accessible. If you want to save files to the server, please secure the '%directory' directory", array(
        '%directory' => $directory,
      ));
      _backup_migrate_message($message, array(), "error");
      return FALSE;
    }
    return $directory;
  }

  /**
   * Check if the given directory is within the webroot and is therefore web accessible.
   */
  function dir_in_webroot($directory) {
    if (strpos(realpath($directory), realpath('.')) !== FALSE) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * Check if a file can be read remotely via http.
   */
  function test_file_readable_remotely($path, $contents) {
    $url = $GLOBALS['base_url'] . '/' . str_replace('\\', '/', $path);
    $result = drupal_http_request($url);
    if (!empty($result->data) && strpos($result->data, $contents) !== FALSE) {
      return TRUE;
    }
    return FALSE;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
backup_migrate_destination::$cache_expire property
backup_migrate_destination::$cache_files property 2
backup_migrate_destination::$db_table property Overrides backup_migrate_item::$db_table
backup_migrate_destination::$default_values property Overrides backup_migrate_item::$default_values
backup_migrate_destination::$destination_type property
backup_migrate_destination::$fetch_time property
backup_migrate_destination::$plural property Overrides backup_migrate_item::$plural
backup_migrate_destination::$singular property Overrides backup_migrate_item::$singular
backup_migrate_destination::$type_name property Overrides backup_migrate_item::$type_name
backup_migrate_destination::backup_settings_default function Get the form for the settings for this filter. 1
backup_migrate_destination::backup_settings_form function Get the form for the settings for this filter. 1
backup_migrate_destination::backup_settings_form_submit function Submit the settings form. Any values returned will be saved.
backup_migrate_destination::backup_settings_form_validate function Get the form for the settings for this filter.
backup_migrate_destination::can_delete_file function Determine if we can read the given file.
backup_migrate_destination::create function Create a new destination of the correct type. Overrides backup_migrate_item::create
backup_migrate_destination::delete_confirm_message function Get the message to send to the user when confirming the deletion of the item. Overrides backup_migrate_item::delete_confirm_message
backup_migrate_destination::file_cache_clear function Retrieve the file list.
backup_migrate_destination::file_cache_get function Retrieve the file list.
backup_migrate_destination::file_cache_set function Cache the file list.
backup_migrate_destination::file_exists function Check if a file exists in the given destination.
backup_migrate_destination::file_types function 1
backup_migrate_destination::get_action_links function Get the action links for a destination. Overrides backup_migrate_item::get_action_links
backup_migrate_destination::get_destination_type_name function Get the type name of this destination for display to the user.
backup_migrate_destination::get_display_location function 2
backup_migrate_destination::get_file_links function Get the action links for a file on a given destination.
backup_migrate_destination::get_list_column_info function Get the columns needed to list the type. Overrides backup_migrate_item::get_list_column_info
backup_migrate_destination::get_list_row function Get a row of data to be used in a list of items of this type. Overrides backup_migrate_item::get_list_row 1
backup_migrate_destination::get_location function 1
backup_migrate_destination::get_menu_items function Add the menu items specific to the destination type. Overrides backup_migrate_item::get_menu_items
backup_migrate_destination::get_name function Get the name of the item. Overrides backup_migrate_item::get_name 1
backup_migrate_destination::op function Does this destination support the given operation.
backup_migrate_destination::ops function
backup_migrate_destination::remove_op function Remove the given op from the support list.
backup_migrate_destination::restore_settings_default function Get the form for the settings for this filter.
backup_migrate_destination::restore_settings_form function Get the form for the settings for this filter.
backup_migrate_destination::restore_settings_form_submit function Submit the settings form. Any values returned will be saved.
backup_migrate_destination::restore_settings_form_validate function Get the form for the settings for this filter.
backup_migrate_destination::settings function
backup_migrate_destination::settings_default function Get the form for the settings for this destination type.
backup_migrate_destination::settings_form function Get the form for the settings for this destination.
backup_migrate_destination::settings_form_submit function Submit the settings form. Any values returned will be saved.
backup_migrate_destination::settings_form_validate function Validate the form for the settings for this destination. 1
backup_migrate_destination::set_location function 1
backup_migrate_destination::set_name function
backup_migrate_destination::set_type function
backup_migrate_destination::strings function This function is not supposed to be called. It is just here to help the po extractor out. Overrides backup_migrate_item::strings
backup_migrate_destination::_delete_file function Delete the file with the given destination specific id. 2
backup_migrate_destination::_list_files function List all the available files in the given destination with their destination specific id. 3
backup_migrate_destination::_save_file function Save the given file to the destination. 2
backup_migrate_destination_files::$supported_ops property Overrides backup_migrate_destination::$supported_ops 2
backup_migrate_destination_files::can_read_file function Determine if we can read the given file. Overrides backup_migrate_destination::can_read_file
backup_migrate_destination_files::check_dir function Prepare the destination directory for the backups.
backup_migrate_destination_files::check_web_dir function Check that a web accessible directory has been properly secured, othewise attempt to secure it.
backup_migrate_destination_files::delete_file function File delete destination callback. Overrides backup_migrate_destination::delete_file
backup_migrate_destination_files::dir_in_webroot function Check if the given directory is within the webroot and is therefore web accessible.
backup_migrate_destination_files::edit_form function Get the form for the settings for the files destination. Overrides backup_migrate_destination::edit_form
backup_migrate_destination_files::edit_form_submit function Submit the form for the settings for the files destination. Overrides backup_migrate_item::edit_form_submit
backup_migrate_destination_files::edit_form_validate function Validate the form for the settings for the files destination. Overrides backup_migrate_item::edit_form_validate
backup_migrate_destination_files::get_file function Get the file object for the given file.
backup_migrate_destination_files::get_filepath function Get the filepath from the given file id.
backup_migrate_destination_files::list_files function File list destination callback. Overrides backup_migrate_destination::list_files
backup_migrate_destination_files::load_file function File load destination callback. Overrides backup_migrate_destination::load_file
backup_migrate_destination_files::save_file function File save destination callback. Overrides backup_migrate_destination::save_file
backup_migrate_destination_files::test_file_readable_remotely function Check if a file can be read remotely via http.
backup_migrate_destination_files::type_name function
backup_migrate_item::$storage property
backup_migrate_item::all_items function Get all of the given items.
backup_migrate_item::decode_db_row function Decode a loaded db row (unserialize necessary fields).
backup_migrate_item::delete function Delete the item from the database.
backup_migrate_item::export function Return as an exported array of values.
backup_migrate_item::from_array function Load an existing item from an array.
backup_migrate_item::generate_id function Return a random (very very likely unique) string id for a new item.
backup_migrate_item::get function Get the member with the given key.
backup_migrate_item::get_actions function Get the rendered action links for a destination.
backup_migrate_item::get_default_values function Get the default values for standard parameters. 2
backup_migrate_item::get_id function Get the primary id for this item (if any is set).
backup_migrate_item::get_list function Get a table of all items of this type. 1
backup_migrate_item::get_list_header function Get header for a lost of this type.
backup_migrate_item::get_primary_key function Get the primary key field title from the schema.
backup_migrate_item::get_schema function Get the schema for the item type.
backup_migrate_item::get_serialized_fields function Return the fields which must be serialized before saving to the db.
backup_migrate_item::item function A particular item.
backup_migrate_item::load_row function Load an existing item from an database (serialized) array.
backup_migrate_item::save function Save the item to the database.
backup_migrate_item::set_id function Set the primary id for this item (if any is set).
backup_migrate_item::to_array function Return as an array of values.
backup_migrate_item::_merge_defaults function Merge parameters with the given defaults.
backup_migrate_item::__construct function Constructor, set the basic info pulled from the db or generated programatically. 4