?AlkantarClanX12
| Current Path : /home/rankinh/mariage-reunion-refonte/wp-content/plugins/backwpup/inc/ |
| Current File : /home/rankinh/mariage-reunion-refonte/wp-content/plugins/backwpup/inc/class-destination-dropbox.php |
<?php
use WPMedia\BackWPup\Backup\FailureContext\Dropbox\DropboxFailureContextMapper;
use WPMedia\BackWPup\Backup\ReasonCode;
/**
* This class allows the user to back up to Dropbox.
*
* Documentation: https://www.dropbox.com/developers/documentation/http/overview
*/
class BackWPup_Destination_Dropbox extends BackWPup_Destinations {
/**
* Service name
*
* @var string
*/
private const SERVICE_NAME = 'Dropbox';
/**
* Dropbox.
*
* Instance of Dropbox API
*
* @var BackWPup_Destination_Dropbox_API|null
*/
protected $dropbox;
/**
* Default Options.
*
* @return array The default options for dropbox
*/
public function option_defaults(): array {
return [
'dropboxtoken' => [],
'dropboxroot' => 'sandbox',
'dropboxmaxbackups' => 15,
'dropboxsyncnodelete' => true,
'dropboxdir' => '/' . trailingslashit( sanitize_file_name( get_bloginfo( 'name' ) ) ),
];
}
/**
* {@inheritdoc}
*
* @param int|array $jobid the job id.
*
* @throws BackWPup_Destination_Dropbox_API_Exception If the destionation instance cannot be created.
*
* @phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
*/
public function edit_form_post_save( $jobid ): void {
$jobids = (array) $jobid;
// Bet auth.
if ( ! empty( $_POST['sandbox_code'] ) ) {
try {
$dropbox = new BackWPup_Destination_Dropbox_API( 'sandbox' );
$dropboxtoken = $dropbox->o_auth_token( $_POST['sandbox_code'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
foreach ( $jobids as $id ) {
BackWPup_Option::update( $id, 'dropboxtoken', $dropboxtoken );
BackWPup_Option::update( $id, 'dropboxroot', 'sandbox' );
}
} catch ( Exception $e ) {
BackWPup_Admin::message( 'DROPBOX: ' . $e->getMessage(), true );
throw $e;
}
}
if ( ! empty( $_POST['dropbbox_code'] ) ) {
try {
$dropbox = new BackWPup_Destination_Dropbox_API( 'dropbox' );
$dropboxtoken = $dropbox->o_auth_token( $_POST['dropbbox_code'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
foreach ( $jobids as $id ) {
BackWPup_Option::update( $id, 'dropboxtoken', $dropboxtoken );
BackWPup_Option::update( $id, 'dropboxroot', 'dropbox' );
}
} catch ( Exception $e ) {
BackWPup_Admin::message( 'DROPBOX: ' . $e->getMessage(), true );
throw $e;
}
}
$_POST['dropboxdir'] = trailingslashit(
str_replace( '//', '/', str_replace( '\\', '/', trim( sanitize_text_field( $_POST['dropboxdir'] ) ) ) ) // phpcs:ignore WordPress.Security
);
if ( '/' === $_POST['dropboxdir'] ) {
$_POST['dropboxdir'] = '';
}
// Delete auth.
if ( ! empty( $_POST['delete_auth'] ) ) {
// We need to check if the token is used on another job.
$temp_jobs_ids = BackWPup_Option::get_job_ids();
$job_token = BackWPup_Option::get( $jobids[0], 'dropboxtoken' );
$is_job_token_used_on_other_job = false;
foreach ( $temp_jobs_ids as $id ) {
// We need to cast the id to int, because the job id is sometimes a string.
if ( (int) $id !== (int) $jobids[0] && BackWPup_Option::get( $id, 'dropboxtoken' ) === $job_token ) {
$is_job_token_used_on_other_job = true;
break;
}
}
// If the token is used on another job, we don't need to revoke it.
if ( ! $is_job_token_used_on_other_job ) {
// Try to revoke the token on dropbox.
try {
$dropbox = $this->get_dropbox( $jobids[0] );
$dropbox->auth_token_revoke();
} catch ( Exception $e ) {
BackWPup_Admin::message(
sprintf(
// translators: %s is the error message.
__( 'Failed to revoke Dropbox token: %s', 'backwpup' ),
esc_html( $e->getMessage() )
),
true
);
}
}
// We delete the token from the jobs and seet the root to sandbox.
foreach ( $jobids as $id ) {
BackWPup_Option::update( $id, 'dropboxtoken', [] );
BackWPup_Option::update( $id, 'dropboxroot', 'sandbox' );
}
}
// Save settings.
foreach ( $jobids as $id ) {
BackWPup_Option::update( $id, 'dropboxsyncnodelete', ! empty( $_POST['dropboxsyncnodelete'] ) );
BackWPup_Option::update(
$id,
'dropboxmaxbackups',
isset( $_POST['dropboxmaxbackups'] ) && is_numeric( $_POST['dropboxmaxbackups'] ) ? absint( $_POST['dropboxmaxbackups'] ) : $this->option_defaults()['dropboxmaxbackups']
);
BackWPup_Option::update( $id, 'dropboxdir', $_POST['dropboxdir'] );
}
}
// phpcs:enable
/**
* Delete File.
*
* @param string $jobdest The destination for this job.
* @param string $backupfile The file to delete.
*/
public function file_delete( string $jobdest, string $backupfile ): void {
$files = get_site_transient( 'backwpup_' . strtolower( $jobdest ) );
[$jobid, $dest] = explode( '_', $jobdest );
try {
$dropbox = $this->get_dropbox( $jobid );
$dropbox->files_delete( [ 'path' => $backupfile ] );
// Update file list.
foreach ( $files as $key => $file ) {
if ( is_array( $file ) && $backupfile === $file['file'] ) {
unset( $files[ $key ] );
}
}
unset( $dropbox );
} catch ( Exception $e ) {
BackWPup_Admin::message( 'DROPBOX: ' . $e->getMessage(), true );
}
$key = 'backwpup_' . strtolower( $jobdest );
/**
* Fires after jobs is updated.
*
* @since 5.2.1
*
* @param string $key The newly backup reference key.
* @param array $files An array of files data.
*/
do_action( 'backwpup_update_backup_history', $key, $files );
}
/**
* File Update List.
*
* Update the list of files in the transient.
*
* @param BackWPup_Job|int $job Either the job object or job ID.
* @param bool $delete Whether to delete old backups.
*/
public function file_update_list( $job, bool $delete = false ): void {
if ( $job instanceof BackWPup_Job ) {
$job_object = $job;
$jobid = $job->job['jobid'];
} else {
$job_object = null;
$jobid = $job;
}
$backupfilelist = [];
$filecounter = 0;
$files = [];
$dropbox = $this->get_dropbox( $jobid );
$files_list = $dropbox->list_folder( BackWPup_Option::get( $jobid, 'dropboxdir' ) );
foreach ( $files_list as $data ) {
if ( 'file' === $data['.tag'] && $this->is_backup_owned_by_job( $data['name'], $jobid ) ) {
$file = $data['name'];
if ( $this->is_backup_archive( $file ) ) {
$backupfilelist[ strtotime( (string) $data['server_modified'] ) ] = $file;
}
$files[ $filecounter ]['folder'] = dirname( (string) $data['path_display'] );
$files[ $filecounter ]['file'] = $data['path_display'];
$files[ $filecounter ]['filename'] = $data['name'];
$files[ $filecounter ]['downloadurl'] = network_admin_url(
'admin.php?page=backwpupbackups&action=downloaddropbox&file=' . $data['path_display'] . '&local_file=' . $data['name'] . '&jobid=' . $jobid
);
$files[ $filecounter ]['filesize'] = $data['size'];
$files[ $filecounter ]['time'] = strtotime( (string) $data['server_modified'] );
++$filecounter;
}
}
if ( $delete && $job_object && BackWPup_Option::get( $jobid, 'dropboxmaxbackups' ) > 0 ) { // Delete old backups.
if ( count( $backupfilelist ) > $job_object->job['dropboxmaxbackups'] ) {
ksort( $backupfilelist );
$numdeltefiles = 0;
$deleted_files = [];
while ( ! empty( $backupfilelist ) ) {
$file = array_shift( $backupfilelist );
if ( count( $backupfilelist ) < $job_object->job['dropboxmaxbackups'] ) {
break;
}
$response = $dropbox->files_delete(
[ 'path' => $job_object->job['dropboxdir'] . $file ]
); // Delete files on Cloud.
foreach ( $files as $key => $filedata ) {
if ( $filedata['file'] === $job_object->job['dropboxdir'] . $file
&& ! empty( $response )
) {
$deleted_files[] = $filedata['filename'];
unset( $files[ $key ] );
break;
}
}
++$numdeltefiles;
}
if ( $numdeltefiles > 0 ) {
$job_object->log(
sprintf(
// translators: %d: number of files.
_n(
'%d file deleted from Dropbox',
'%d files deleted on Dropbox',
$numdeltefiles,
'backwpup'
),
$numdeltefiles
),
E_USER_NOTICE
);
}
parent::remove_file_history_from_database( $deleted_files, 'DROPBOX' );
}
}
$key = 'backwpup_' . $jobid . '_dropbox';
/**
* Fires after jobs is updated.
*
* @since 5.2.1
*
* @param string $key The newly backup reference key.
* @param array $files An array of files data.
*/
do_action( 'backwpup_update_backup_history', $key, $files );
}
/**
* Run the archive job for Dropbox.
*
* @param BackWPup_Job $job_object Job object.
*
* @return bool
*/
public function job_run_archive( BackWPup_Job $job_object ): bool {
$job_object->substeps_todo = 2 + $job_object->backup_filesize;
if ( $job_object->steps_data[ $job_object->step_working ]['SAVE_STEP_TRY'] !== $job_object->steps_data[ $job_object->step_working ]['STEP_TRY'] ) {
$job_object->log(
sprintf(
/* translators: %d: attempt number. */
__( '%d. Try to send backup file to Dropbox …', 'backwpup' ),
$job_object->steps_data[ $job_object->step_working ]['STEP_TRY']
)
);
}
try {
$dropbox = $this->get_dropbox( $job_object );
// Get account info.
if ( $job_object->steps_data[ $job_object->step_working ]['SAVE_STEP_TRY'] !== $job_object->steps_data[ $job_object->step_working ]['STEP_TRY'] ) {
$info = $dropbox->users_get_current_account();
if ( ! empty( $info['account_id'] ) ) {
if ( $job_object->is_debug() ) {
$user = $info['name']['display_name'] . ' (' . $info['email'] . ')';
} else {
$user = $info['name']['display_name'];
}
$job_object->log(
sprintf(
/* translators: %s: Dropbox user. */
__( 'Authenticated with Dropbox of user: %s', 'backwpup' ),
$user
)
);
// Quota.
if ( $job_object->is_debug() ) {
$quota = $dropbox->users_get_space_usage();
$dropbox_free_space = $this->free_space_from_quota( is_array( $quota ) ? $quota : [] );
if ( null !== $dropbox_free_space ) {
$job_object->log(
sprintf(
/* translators: %s: available space. */
__( '%s available on your Dropbox', 'backwpup' ),
size_format( $dropbox_free_space, 2 )
)
);
} else {
$job_object->log( __( 'Dropbox free space could not be determined from the quota response.', 'backwpup' ) );
}
}
} else {
$job_object->log(
__( 'Not Authenticated with Dropbox!', 'backwpup' ),
E_USER_ERROR,
__FILE__,
__LINE__,
[
'reason_code' => ReasonCode::REASON_INCORRECT_LOGIN,
'destination' => 'DROPBOX',
'provider_code' => 'not_authenticated',
]
);
return false;
}
$job_object->log( __( 'Uploading to Dropbox …', 'backwpup' ) );
}
// Put the file.
if ( $job_object->substeps_done <= $job_object->backup_filesize ) { // Only if upload not complete.
$errors_before = $job_object->errors;
$response = $dropbox->upload(
$job_object->backup_folder . $job_object->backup_file,
$job_object->job['dropboxdir'] . $job_object->backup_file
);
if ( ! is_array( $response ) || ! isset( $response['size'] ) ) {
if ( $job_object->errors > $errors_before ) {
return false;
}
$fallback = $this->upload_failure_fallback( $job_object, $dropbox );
$message = $fallback['message'];
$context = $fallback['context'];
if ( is_array( $response ) && ! empty( $response['error'] ) ) {
$message .= ' ' . $response['error'];
}
$job_object->log(
$message,
E_USER_ERROR,
__FILE__,
__LINE__,
$context
);
return false;
}
if ( $job_object->backup_filesize === $response['size'] ) {
if ( ! empty( $job_object->job['jobid'] ) ) {
BackWPup_Option::update(
$job_object->job['jobid'],
'lastbackupdownloadurl',
network_admin_url(
'admin.php'
) . '?page=backwpupbackups&action=downloaddropbox&file=' . ltrim(
(string) $response['path_display'],
'/'
) . '&jobid=' . $job_object->job['jobid']
);
}
$job_object->substeps_done = 1 + $job_object->backup_filesize;
$job_object->log(
sprintf(
/* translators: %s: destination path. */
__( 'Backup transferred to %s', 'backwpup' ),
$response['path_display']
),
E_USER_NOTICE
);
} else {
if ( $response['size'] !== $job_object->backup_filesize ) {
$job_object->log(
__( 'Uploaded file size and local file size don\'t match.', 'backwpup' ),
E_USER_ERROR
);
} else {
$job_object->log(
sprintf(
/* translators: %s: destination service name. */
__( 'Error transfering backup to %s.', 'backwpup' ) . ' ' . $response['error'],
__( 'Dropbox', 'backwpup' )
),
E_USER_ERROR
);
}
return false;
}
}
$this->file_update_list( $job_object, true );
} catch ( Exception $e ) {
$job_object->log(
sprintf(
/* translators: %s: error message. */
__( 'Dropbox API: %s', 'backwpup' ),
$e->getMessage()
),
E_USER_ERROR,
$e->getFile(),
$e->getLine(),
$this->error_context( $e )
);
return false;
}
++$job_object->substeps_done;
return true;
}
/**
* Check if Dropbox destination can run.
*
* @param array $job_settings
* @param bool $test_connection Job settings.
*
* @return bool
*/
public function can_run( array $job_settings, bool $test_connection = true ): bool {
return ! ( empty( $job_settings['dropboxtoken'] ) );
}
/**
* Get Dropbox.
*
* Gets the Dropbox API instance.
*
* @param BackWPup_Job|int $job Either the job object or job ID.
*
* @return BackWPup_Destination_Dropbox_API
*/
protected function get_dropbox( $job ): BackWPup_Destination_Dropbox_API {
if ( ! $this->dropbox ) {
if ( $job instanceof BackWPup_Job ) {
$this->dropbox = new BackWPup_Destination_Dropbox_API( $job->job['dropboxroot'], $job );
$jobid = $job->job['jobid'];
$token = $job->job['dropboxtoken'];
} else {
$this->dropbox = new BackWPup_Destination_Dropbox_API( BackWPup_Option::get( $job, 'dropboxroot' ) );
$jobid = $job;
$token = BackWPup_Option::get( $job, 'dropboxtoken' );
}
$this->dropbox->set_o_auth_tokens(
$token,
function ( $token ) use ( $jobid ): void {
BackWPup_Option::update( $jobid, 'dropboxtoken', $token );
}
);
}
return $this->dropbox;
}
/**
* Get service name
*/
public function get_service_name(): string {
return self::SERVICE_NAME;
}
/**
* Build error context for Dropbox errors.
*
* @param \Exception $e Exception object.
* @return array
*/
private function error_context( \Exception $e ): array {
if ( $e instanceof BackWPup_Destination_Dropbox_API_Exception ) {
$context = $e->getContext();
if ( ! empty( $context ) ) {
return $context;
}
}
$mapper = new DropboxFailureContextMapper();
return $mapper->map_reason( ReasonCode::REASON_UNKNOWN_ERROR );
}
/**
* Build the narrow upload fallback used only when the API layer did not log a structured failure.
*
* @param BackWPup_Job $job_object Current job object.
* @param BackWPup_Destination_Dropbox_API $dropbox Dropbox API instance.
* @return array
*/
private function upload_failure_fallback( BackWPup_Job $job_object, BackWPup_Destination_Dropbox_API $dropbox ): array {
$fallback = [
'message' => __( 'Dropbox API: Upload failed.', 'backwpup' ),
'context' => [
'destination' => 'DROPBOX',
],
];
try {
$quota = $dropbox->users_get_space_usage();
} catch ( Exception $exception ) {
$quota = null;
}
$free_space = is_array( $quota ) ? $this->free_space_from_quota( $quota ) : null;
if ( null !== $free_space && $job_object->backup_filesize > $free_space ) {
$fallback = [
'message' => __( 'You do not have enough space in your Dropbox.', 'backwpup' ),
'context' => [
'reason_code' => ReasonCode::REASON_NOT_ENOUGH_STORAGE,
'destination' => 'DROPBOX',
'provider_code' => 'insufficient_space',
],
];
}
return $fallback;
}
/**
* Extract available Dropbox space from the quota payload.
*
* @param array $quota Dropbox quota payload.
* @return int|null
*/
private function free_space_from_quota( array $quota ): ?int {
if (
! isset( $quota['allocation']['allocated'], $quota['used'] )
|| ! is_numeric( $quota['allocation']['allocated'] )
|| ! is_numeric( $quota['used'] )
) {
return null;
}
return max( 0, (int) $quota['allocation']['allocated'] - (int) $quota['used'] );
}
}