.dream.io`. * * @var string */ const ENDPOINT_REGEX = '^s3\.[0-9a-z_-]+\.dream\.io$'; /** * Class constructor */ public function __construct() { add_action('updraftplus_admin_enqueue_scripts', array($this, 'updraftplus_admin_enqueue_scripts')); } /** * Enqueue scripts on UpdraftPlus settings page. * * @return void */ public function updraftplus_admin_enqueue_scripts() { global $updraftplus; $updraftplus->enqueue_select2(); } /** * Returns endpoint options. * * @return array */ public static function get_endpoints() { // When new endpoint introduced in future, Please add it here and also add it as hard coded option for endpoint dropdown in self::get_partial_configuration_template_for_endpoint() // Put the default first return array( // Endpoint, then the label 's3.us-east-005.dream.io' => 's3.us-east-005.dream.io', 'objects-us-east-1.dream.io' => 'objects-us-east-1.dream.io ('.__('Permanently unavailable on Nov 12th, 2025', 'updraftplus').')', ); } protected $use_v4 = false; /** * Given an S3 object, possibly set the region on it * * @param Object $obj - like UpdraftPlus_S3 * @param String $region - or empty to fetch one from saved configuration * @param String $bucket_name */ protected function set_region($obj, $region = '', $bucket_name = '') {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $bucket_name $config = $this->get_config(); $endpoint = ('' != $region && 'n/a' != $region) ? $region : $config['endpoint']; global $updraftplus; if ($updraftplus->backup_time) { $updraftplus->log("Set endpoint (".get_class($obj)."): $endpoint"); // Warning for objects-us-east-1 shutdown in Nov 2025 if ('objects-us-east-1.dream.io' == $endpoint) { $updraftplus->log("The objects-us-east-1.dream.io endpoint is permanently unavailable since Nov. 12th, 2025. Please switch to a new endpoint as suggested in: https://help.dreamhost.com/hc/en-us/articles/360001370846-What-DreamObjects-hostname-should-I-use-to-connect", 'warning', 'dreamobjects_east_shutdown'); } } $obj->setEndpoint($endpoint); } /** * This method overrides the parent method and lists the supported features of this remote storage option. * * @return Array - an array of supported features (any features not mentioned are asuumed to not be supported) */ public function get_supported_features() { // This options format is handled via only accessing options via $this->get_options() return array('multi_options', 'config_templates', 'multi_storage', 'conditional_logic'); } /** * Retrieve default options for this remote storage module. * * @return Array - an array of options */ public function get_default_options() { return array( 'accesskey' => '', 'secretkey' => '', 'path' => '', ); } /** * Retrieve specific options for this remote storage module * * @param Boolean $force_refresh - if set, and if relevant, don't use cached credentials, but get them afresh * * @return Array - an array of options */ protected function get_config($force_refresh = false) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $force_refresh unused $opts = $this->get_options(); $opts['whoweare'] = 'DreamObjects'; $opts['whoweare_long'] = 'DreamObjects'; $opts['key'] = 'dreamobjects'; if (empty($opts['endpoint'])) { $endpoints = array_keys(self::get_endpoints()); $opts['endpoint'] = $endpoints[0]; } return $opts; } /** * Get the pre configuration template */ public function get_pre_configuration_template() { ?> {{method_display_name}}
{{{xmlwriter_existence_label}}} {{{simplexmlelement_existence_label}}} {{{curl_existence_label}}}
{{{console_url_text}}}

{{ssl_error_text}}

get_configuration_template_engine('dreamobjects', 'DreamObjects', 'DreamObjects', 'DreamObjects', 'https://panel.dreamhost.com/index.cgi?tree=storage.dreamhostobjects', 'DreamObjects'); ob_start(); ?> {{input_accesskey_label}}: {{input_secretkey_label}}: {{input_location_label}}: {{method_id}}:// {{input_endpoint_label}} {{invalid_endpoint_error_message}} {{{get_template_test_button_html "DreamObjects"}}} show_double_warning( ''.__('Warning', 'updraftplus').': '. /* translators: %s: missing PHP module */ sprintf(__("Your web server's PHP installation does not include a required module (%s).", 'updraftplus'), 'SimpleXMLElement').' '. __("Please contact your web hosting provider's support.", 'updraftplus').' '. /* translators: 1: module description, 2: required module */ sprintf(__('UpdraftPlus\'s %1$s module requires %2$s.', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()], 'SimpleXMLElement').' '. __('Please do not file any support requests; there is no alternative.', 'updraftplus'), $this->get_id(), false ), $this->allowed_html_for_content_sanitisation() ); } else { $simplexmlelement_existence_label = ''; } if (!apply_filters('updraftplus_dreamobjects_xmlwriter_exists', 'UpdraftPlus_S3_Compat' != $this->indicate_s3_class() || !class_exists('XMLWriter'))) { $xmlwriter_existence_label = wp_kses( $updraftplus_admin->show_double_warning( ''.__('Warning', 'updraftplus').': '. /* translators: %s: missing PHP module */ sprintf(__("Your web server's PHP installation does not included a required module (%s).", 'updraftplus'), 'XMLWriter').' '. __("Please contact your web hosting provider's support and ask for them to enable it.", 'updraftplus'), $this->get_id(), false ), $this->allowed_html_for_content_sanitisation() ); } else { $xmlwriter_existence_label = ''; } $properties = array( 'storage_image_url' => UPDRAFTPLUS_URL."/images/dreamobjects_logo-horiz-2013.png", 'curl_existence_label' => wp_kses($updraftplus_admin->curl_check($updraftplus->backup_methods[$this->get_id()], false, $this->get_id()." hidden-in-updraftcentral", false), $this->allowed_html_for_content_sanitisation()), 'simplexmlelement_existence_label' => $simplexmlelement_existence_label, 'xmlwriter_existence_label' => $xmlwriter_existence_label, 'console_url_text' => sprintf( /* translators: 1: console URL, 2: service name, 3: service name */ __('Get your access key and secret key from your %2$s console, then pick a (globally unique - all %3$s users) bucket name (letters and numbers) (and optionally a path) to use for storage.', 'updraftplus'), 'https://panel.dreamhost.com/index.cgi?tree=storage.dreamhostobjects', $updraftplus->backup_methods[$this->get_id()], $updraftplus->backup_methods[$this->get_id()] ).' '.__('This bucket will be created for you if it does not already exist.', 'updraftplus'), 'updraftplus_com_link' => apply_filters("updraftplus_com_link", "https://teamupdraft.com/documentation/updraftplus/topics/backing-up/troubleshooting/i-get-ssl-certificate-errors-when-backing-up-and-or-restoring/?utm_source=udp-plugin&utm_medium=referral&utm_campaign=paac&utm_content=dreamobjects-ssl-certificates&utm_creative_format=text"), 'ssl_error_text' => __('If you see errors about SSL certificates, then please go here for help.', 'updraftplus'), 'credentials_creation_link_text' => __('Create Azure credentials in your Azure developer console.', 'updraftplus'), 'configuration_helper_link_text' => __('For more detailed instructions, follow this link.', 'updraftplus'), /* translators: %s: service name */ 'input_accesskey_label' => sprintf(__('%s access key', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]), /* translators: %s: service name */ 'input_secretkey_label' => sprintf(__('%s secret key', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]), 'input_secretkey_type' => apply_filters('updraftplus_admin_secret_field_type', 'password'), /* translators: %s: service name */ 'input_location_label' => sprintf(__('%s location', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]), 'input_location_title' => __('Enter only a bucket name or a bucket and path.', 'updraftplus').' '.__('Examples: mybucket, mybucket/mypath', 'updraftplus'), /* translators: %s: service name */ 'input_endpoint_label' => sprintf(__('%s end-point', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]), /* translators: %s: service name */ 'input_test_label' => sprintf(__('Test %s Settings', 'updraftplus'), $updraftplus->backup_methods[$this->get_id()]), /* translators: %s: Desired endpoint format.*/ 'invalid_endpoint_error_message' => sprintf(__('Custom endpoint should be in the following format "%s".', 'updraftplus'), 's3..dream.io'), ); return wp_parse_args($properties, $this->get_persistent_variables_and_methods()); } /** * Ensure that only the DreamObjects endpoints (objects-.dream.io and s3..dream.io) are allowed and that signature header version 4 must exclusively be used for s3..dream.io enpoint * * @param Object $storage S3 name * @param Array $config array of config details; if the provider does not have the concept of regions, then the key 'endpoint' is required to be set * @param String $bucket S3 Bucket * @param String $path S3 Path * * @return Array - N.B. May contain updated versions of $storage and $config */ protected function get_bucket_access($storage, $config, $bucket, $path) { if (empty($config['endpoint']) || !self::is_valid_endpoint($config['endpoint'])) throw new Exception('Invalid DreamObjects endpoint: '.$config['endpoint']); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- The escaping should happen when the exception is caught and printed if (preg_match('/'.self::ENDPOINT_REGEX.'/i', trim($config['endpoint']))) { $this->use_v4 = true; $storage->setSignatureVersion('v4'); $storage->useDNSBucketName(false); } return parent::get_bucket_access($storage, $config, $bucket, $path); } /** * Perform a test of user-supplied credentials, and echo the result. * * @param array $posted_settings Settings to test. * * @return void Echo the result of credentials test. */ public function credentials_test($posted_settings) { if (!empty($posted_settings['endpoint']) && !self::is_valid_endpoint($posted_settings['endpoint'])) { /* translators: 1: Invalid custom endpoint, 2: Expected endpoint format */ echo sprintf(esc_html__('Failure: Custom endpoint "%1$s" is not in the desired format "%2$s".', 'updraftplus'), $posted_settings['endpoint'], 's3..dream.io'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Prevent escaping '<' & '>' in endpoint as this message is shown in alert. return; } parent::credentials_test($posted_settings); } /** * Sanitization filter for saving DreamObjects settings. * * @param array $new_settings New settings passed by user. * * @return array Sanitized settings to be saved in DB. */ public function options_filter($new_settings) { $current_settings = UpdraftPlus_Options::get_updraft_option('updraft_dreamobjects', array()); // Previous settings would be empty on initial load. if (empty($current_settings)) return parent::options_filter($new_settings); $current_settings = $current_settings['settings']; // Check if endpoint is updated to an invalid format, then log it. foreach ($new_settings['settings'] as $instance_id => $new_storage_options) { if (isset($current_settings[$instance_id]['endpoint'], $new_storage_options['endpoint']) && $current_settings[$instance_id]['endpoint'] !== $new_storage_options['endpoint'] && !self::is_valid_endpoint($new_storage_options['endpoint']) ) { $msg = sprintf('Custom endpoint "%s" is not in the format "s3..dream.io".', esc_html($new_storage_options['endpoint'])); $this->log($msg, 'error'); error_log('UpdraftPlus: DreamObjects: '.$msg); } } return parent::options_filter($new_settings); } /** * Check if valid endpoint. * * @param string $endpoint DreamObjects endpoint provided by user. * * @return bool True for valid endpoint else false. */ public static function is_valid_endpoint($endpoint) { $endpoint = trim($endpoint); $endpoints = self::get_endpoints(); if (isset($endpoints[$endpoint]) || preg_match('/'.self::ENDPOINT_REGEX.'/i', $endpoint)) return true; return false; } }