. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . AnonSec Shell
AnonSec Shell
Server IP : 94.23.64.18  /  Your IP : 216.73.216.185   [ Reverse IP ]
Web Server : Apache
System : Linux webm005.cluster107.gra.hosting.ovh.net 5.15.167-ovh-vps-grsec-zfs-classid #1 SMP Tue Sep 17 08:14:20 UTC 2024 x86_64
User : villadal ( 6036)
PHP Version : 7.4.33
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
Domains : 2 Domains
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/villadal/www/old/wp-content/plugins/backwpup/inc/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     [ BACKUP SHELL ]     [ JUMPING ]     [ MASS DEFACE ]     [ SCAN ROOT ]     [ SYMLINK ]     

Current File : /home/villadal/www/old/wp-content/plugins/backwpup/inc/class-destination-dropbox-api.php
<?php

use Inpsyde\BackWPup\Http\Client\WpHttpClient;
use Inpsyde\BackWPup\Http\Message\RequestFactory;
use Inpsyde\BackWPup\Http\Message\ResponseFactory;
use Inpsyde\BackWPup\Http\Message\StreamFactory;
use Inpsyde\BackWPup\Http\Message\Decorator\FormRequest;
use Inpsyde\BackWPup\Http\Message\Decorator\JsonRequest;
use Inpsyde\BackWPup\Http\Message\Decorator\AuthorizationRequest;
use Psr\Http\Message\ResponseInterface;

/**
 * Class for communicating with Dropbox API V2.
 */
class BackWPup_Destination_Dropbox_API
{

	/**
	 * URL to Dropbox API endpoint.
	 */
	const API_URL = 'https://api.dropboxapi.com/';

	/**
	 * URL to Dropbox content endpoint.
	 */
	const API_CONTENT_URL = 'https://content.dropboxapi.com/';

	/**
	 * URL to Dropbox for authentication.
	 */
	const API_WWW_URL = 'https://www.dropbox.com/';

	/**
	 * API version.
	 */
	const API_VERSION_URL = '2/';

	/**
	 * oAuth vars
	 *
	 * @var string
	 */
	private $oauthAppKey = '';

	/**
	 * @var string
	 */
	private $oauthAppSecret = '';

	/**
	 * @var string
	 */
	private $oauthToken = [];

	/**
	 * Job object for logging.
	 *
	 * @var BackWPup_Job
	 */
	private $jobObject;

	/**
	 * Callback to call when token is refreshed
	 *
	 * @var callback
	 */
	private $listener;

	/**
	 * The user agent to use in Dropbox requests
	 *
	 * @var string
	 */
	private $userAgent;

	/**
	 * A path to the SSL ca-bundle file to use in Dropbox requests
	 *
	 * @var string
	 */
	private $caBundle;

	/**
	 * @param string $boxtype
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Exception
	 */
	public function __construct($boxtype = 'dropbox', BackWPup_Job $jobObject = null)
	{
		if ($boxtype === 'dropbox') {
			$this->oauthAppKey = get_site_option(
				'backwpup_cfg_dropboxappkey',
				base64_decode('NXdtdXl0cm5qZzB5aHhw')
			);
			$this->oauthAppSecret = BackWPup_Encryption::decrypt(
				get_site_option('backwpup_cfg_dropboxappsecret', base64_decode('cXYzZmp2N2IxcG1rbWxy'))
			);
		} else {
			$this->oauthAppKey = get_site_option(
				'backwpup_cfg_dropboxsandboxappkey',
				base64_decode('a3RqeTJwdXFwZWVydW92')
			);
			$this->oauthAppSecret = BackWPup_Encryption::decrypt(
				get_site_option('backwpup_cfg_dropboxsandboxappsecret', base64_decode('aXJ1eDF3Ym9mMHM5eGp6'))
			);
		}

		if (empty($this->oauthAppKey) || empty($this->oauthAppSecret)) {
			throw new BackWPup_Destination_Dropbox_API_Exception("No App key or App Secret specified.");
		}

		$this->jobObject = $jobObject;
	}

	/**
	 * List a folder
	 *
	 * This is a functions method to use filesListFolder and
	 * filesListFolderContinue to construct an array of files within a given
	 * folder path.
	 *
	 * @param string $path
	 *
	 * @return array
	 */
	public function listFolder($path)
	{
		$files = [];
		$result = $this->filesListFolder([ 'path' => $path ]);

		if (!$result) {
			return [];
		}

		$files = array_merge($files, $result['entries']);

		$args = [ 'cursor' => $result['cursor'] ];

		while ($result['has_more'] == true) {
			$result = $this->filesListFolderContinue($args);
			$files = array_merge($files, $result['entries']);
		}

		return $files;
	}

	/**
	 * Uploads a file to Dropbox.
	 *
	 * @param $file
	 * @param string $path
	 * @param bool   $overwrite
	 *
	 * @return array
	 * @throws BackWPup_Destination_Dropbox_API_Exception
	 */
	public function upload($file, $path = '', $overwrite = true)
	{
		$file = str_replace("\\", "/", $file);

		if (!is_readable($file)) {
			throw new BackWPup_Destination_Dropbox_API_Exception(
				"Error: File \"$file\" is not readable or doesn't exist."
			);
		}

		if (filesize($file) < 5242880) { //chunk transfer on bigger uploads
			$output = $this->filesUpload(
				[
					'contents' => file_get_contents($file),
					'path' => $path,
					'mode' => ( $overwrite ) ? 'overwrite' : 'add',
				]
			);
		} else {
			$output = $this->multipartUpload($file, $path, $overwrite);
		}

		return $output;
	}

	/**
	 * @param $file
	 * @param string $path
	 * @param bool   $overwrite
	 *
	 * @return array|mixed|string
	 * @throws BackWPup_Destination_Dropbox_API_Exception
	 */
	public function multipartUpload($file, $path = '', $overwrite = true)
	{
		$file = str_replace("\\", "/", $file);

		if (!is_readable($file)) {
			throw new BackWPup_Destination_Dropbox_API_Exception(
				"Error: File \"$file\" is not readable or doesn't exist."
			);
		}

		$chunkSize = 4194304; //4194304 = 4MB

		$fileHandle = fopen($file, 'rb');
		if (!$fileHandle) {
			throw new BackWPup_Destination_Dropbox_API_Exception("Can not open source file for transfer.");
		}

		if (!isset($this->jobObject->steps_data[ $this->jobObject->step_working ]['uploadid'])) {
			$this->jobObject->log(__('Beginning new file upload session', 'backwpup'));
			$session = $this->filesUploadSessionStart(
			);
			$this->jobObject->steps_data[ $this->jobObject->step_working ]['uploadid'] = $session['session_id'];
		}
		if (!isset($this->jobObject->steps_data[ $this->jobObject->step_working ]['offset'])) {
			$this->jobObject->steps_data[ $this->jobObject->step_working ]['offset'] = 0;
		}
		if (!isset($this->jobObject->steps_data[ $this->jobObject->step_working ]['totalread'])) {
			$this->jobObject->steps_data[ $this->jobObject->step_working ]['totalread'] = 0;
		}

		//seek to current position
		if ($this->jobObject->steps_data[ $this->jobObject->step_working ]['offset'] > 0) {
			fseek($fileHandle, $this->jobObject->steps_data[ $this->jobObject->step_working ]['offset']);
		}

		while ($data = fread($fileHandle, $chunkSize)) {
			$chunkUploadStart = microtime(true);

			if ($this->jobObject->is_debug()) {
				$this->jobObject->log(
					sprintf(__('Uploading %s of data', 'backwpup'), size_format(strlen($data)))
				);
			}

			$this->filesUploadSessionAppendV2(
				[
					'contents' => $data,
					'cursor' => [
						'session_id' => $this->jobObject->steps_data[ $this->jobObject->step_working ]['uploadid'],
						'offset' => $this->jobObject->steps_data[ $this->jobObject->step_working ]['offset'],
					],
				]
			);
			$chunkUploadTime = microtime(
				true
			) - $chunkUploadStart;
			$this->jobObject->steps_data[ $this->jobObject->step_working ]['totalread'] += strlen($data);

			//args for next chunk
			$this->jobObject->steps_data[ $this->jobObject->step_working ]['offset'] += $chunkSize;
			if ($this->jobObject->job['backuptype'] === 'archive') {
				$this->jobObject->substeps_done = $this->jobObject->steps_data[ $this->jobObject->step_working ]['offset'];
				if (strlen($data) == $chunkSize) {
					$timeRemaining = $this->jobObject->do_restart_time();
					//calc next chunk
					if ($timeRemaining < $chunkUploadTime) {
						$chunkSize = floor($chunkSize / $chunkUploadTime * ( $timeRemaining - 3 ));
						if ($chunkSize < 0) {
							$chunkSize = 1024;
						}
						if ($chunkSize > 4194304) {
							$chunkSize = 4194304;
						}
					}
				}
			}
			$this->jobObject->update_working_data();
			//correct position
			fseek($fileHandle, $this->jobObject->steps_data[ $this->jobObject->step_working ]['offset']);
		}

		fclose($fileHandle);

		$this->jobObject->log(
			sprintf(
				__('Finishing upload session with a total of %s uploaded', 'backwpup'),
				size_format($this->jobObject->steps_data[ $this->jobObject->step_working ]['totalread'])
			)
		);
		$response = $this->filesUploadSessionFinish(
			[
				'cursor' => [
					'session_id' => $this->jobObject->steps_data[ $this->jobObject->step_working ]['uploadid'],
					'offset' => $this->jobObject->steps_data[ $this->jobObject->step_working ]['totalread'],
				],
				'commit' => [
					'path' => $path,
					'mode' => ( $overwrite ) ? 'overwrite' : 'add',
				],
			]
		);

		unset($this->jobObject->steps_data[ $this->jobObject->step_working ]['uploadid']);
		unset($this->jobObject->steps_data[ $this->jobObject->step_working ]['offset']);

		return $response;
	}

	/**
	 * Set the oauth tokens for this request.
	 *
	 * @param array $token The array with access and refresh tokens
	 * @param callable $listener The callback to be called when a new token is fetched
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Exception
	 */
	public function setOAuthTokens(array $token, $listener = null)
	{
		if (empty($token['access_token'])) {
			throw new BackWPup_Destination_Dropbox_API_Exception(
				__('No access token provided', 'backwpup')
			);
		}

		if (empty($token['refresh_token'])) {
			throw new BackWPup_Destination_Dropbox_API_Exception(
				__('No refresh token provided. You may need to reauthenticate with Dropbox', 'backwpup')
			);
		}

		$this->oauthToken = $token;

		if (isset($listener) && is_callable($listener)) {
			$this->listener = $listener;
		}

		if (isset($token['expires']) && time() > $token['expires']) {
			$token = $this->refresh($token['refresh_token']);
			$this->notifyRefresh($token);
		}
	}

	/**
	 * Get the current token array
	 *
	 * Also modifies expires_in to match how much time is left until the access token expires.
	 *
	 * @throws BadMethodCallException If tokens have not been set
	 * @return array The token array
	 */
	public function getTokens()
	{
		$now = time();
		$tokens = $this->oauthToken;
		if (empty($tokens)) {
			throw new \BadMethodCallException(
				__('OAuth tokens have not been set.', 'backwpup')
			);
		}

		if ($tokens['expires'] > $now) {
			$tokens['expires_in'] = $tokens['expires'] - $now;
		} else {
			$tokens = $this->refresh($tokens['refresh_token']);
			$this->notifyRefresh($tokens);
		}

		return $tokens;
	}

	/**
	 * Returns the URL to authorize the user.
	 *
	 * @return string The authorization URL
	 */
	public function oAuthAuthorize()
	{
		return self::API_WWW_URL . 'oauth2/authorize?response_type=code&client_id=' . $this->oauthAppKey . '&token_access_type=offline';
	}

	/**
	 * Takes the oauth code and returns the access token.
	 *
	 * @param string $code The oauth code
	 *
	 * @return array An array including the access token, account ID, expiration, and
	 *               other information.
	 */
	public function oAuthToken($code)
	{
		$token = $this->request(
			'oauth2/token',
			[
				'code' => trim($code),
				'grant_type' => 'authorization_code',
			],
			'oauth'
		);

		$token['expires'] = time() + $token['expires_in'];

		return $token;
	}

	/**
	 * Returns a new access token given the refresh token.
	 *
	 * @param string $refreshToken The refresh token
	 *
	 * @return array An array including the access token, account ID, expiration, and
	 *               other information.
	 */
	public function refresh($refreshToken)
	{
		$token = $this->request(
			'oauth2/token',
			[
				'refresh_token' => trim($refreshToken),
				'grant_type' => 'refresh_token',
			],
			'oauth'
		);

		$token['expires'] = time() + $token['expires_in'];

		$this->oauthToken = array_merge($this->oauthToken, $token);

		$this->log(
			__('Token has expired; new token has been obtained', 'backwpup')
		);

		return $this->oauthToken;
	}

	/**
	 * Notifies the listener that the access token was refreshed
	 *
	 * @param array $token The new token
	 */
	private function notifyRefresh(array $token)
	{
		if (isset($this->listener)) {
			call_user_func($this->listener, $token);
		}
	}

	/**
	 * Revokes the auth token.
	 *
	 * @return array
	 */
	public function authTokenRevoke()
	{
		return $this->request('auth/token/revoke');
	}

	/**
	 * Download
	 *
	 * @param array $args Argument for the api request.
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Exception Because of a rate limit.
	 * @return mixed Whatever the api request returns.
	 */
	public function download($args, $startByte = null, $endByte = null)
	{
		$args['path'] = $this->formatPath($args['path']);

		if ($startByte !== null && $endByte !== null) {
			return $this->request('files/download', $args, 'download', false, "{$startByte}-{$endByte}");
		}

		return $this->request('files/download', $args, 'download');
	}

	/**
	 * Deletes a file.
	 *
	 * @param array $args An array of arguments
	 *
	 * @return array Information on the deleted file
	 */
	public function filesDelete($args)
	{
		$args['path'] = $this->formatPath($args['path']);

		try {
			return $this->request('files/delete', $args);
		} catch (BackWPup_Destination_Dropbox_API_Request_Exception $e) {
			$this->handleFilesDeleteError($e->getError());
		}
	}

	/**
	 * Gets the metadata of a file.
	 *
	 * @param array $args An array of arguments
	 *
	 * @return array The file's metadata
	 */
	public function filesGetMetadata($args)
	{
		$args['path'] = $this->formatPath($args['path']);
		try {
			return $this->request('files/get_metadata', $args);
		} catch (BackWPup_Destination_Dropbox_API_Request_Exception $e) {
			$this->handleFilesGetMetadataError($e->getError());
		}
	}

	/**
	 * Gets a temporary link from Dropbox to access the file.
	 *
	 * @param array $args An array of arguments
	 *
	 * @return array Information on the file and link
	 */
	public function filesGetTemporaryLink($args)
	{
		$args['path'] = $this->formatPath($args['path']);
		try {
			return $this->request('files/get_temporary_link', $args);
		} catch (BackWPup_Destination_Dropbox_API_Request_Exception $e) {
			$this->handleFilesGetTemporaryLinkError($e->getError());
		}
	}

	/**
	 * Lists all the files within a folder.
	 *
	 * @param array $args An array of arguments
	 *
	 * @return array A list of files
	 */
	public function filesListFolder($args)
	{
		$args['path'] = $this->formatPath($args['path']);
		try {
			return $this->request('files/list_folder', $args);
		} catch (BackWPup_Destination_Dropbox_API_Request_Exception $e) {
			$this->handleFilesListFolderError($e->getError());
		}
	}

	/**
	 * Continue to list more files.
	 *
	 * When a folder has a lot of files, the API won't return all at once.
	 * So this method is to fetch more of them.
	 *
	 * @param array $args An array of arguments
	 *
	 * @return array An array of files
	 */
	public function filesListFolderContinue($args)
	{
		try {
			return $this->request('files/list_folder/continue', $args);
		} catch (BackWPup_Destination_Dropbox_API_Request_Exception $e) {
			$this->handleFilesListFolderContinueError($e->getError());
		}
	}

	/**
	 * Uploads a file to Dropbox.
	 *
	 * The file must be no greater than 150 MB.
	 *
	 * @param array $args An array of arguments
	 *
	 * @return array    The uploaded file's information.
	 */
	public function filesUpload($args)
	{
		$args['path'] = $this->formatPath($args['path']);

		if (isset($args['client_modified'])
			&& $args['client_modified'] instanceof DateTime
		) {
			$args['client_modified'] = $args['client_modified']->format('Y-m-d\TH:m:s\Z');
		}

		try {
			return $this->request('files/upload', $args, 'upload');
		} catch (BackWPup_Destination_Dropbox_API_Request_Exception $e) {
			$this->handleFilesUploadError($e->getError());
		}
	}

	/**
	 * Append more data to an uploading file
	 *
	 * @param array $args An array of arguments
	 */
	public function filesUploadSessionAppendV2($args)
	{
		try {
			return $this->request(
				'files/upload_session/append_v2',
				$args,
				'upload'
			);
		} catch (BackWPup_Destination_Dropbox_API_Request_Exception $e) {
			$error = $e->getError();

			// See if we can fix the error first
			if ($error['.tag'] === 'incorrect_offset') {
				$args['cursor']['offset'] = $error['correct_offset'];

				return $this->request(
					'files/upload_session/append_v2',
					$args,
					'upload'
				);
			}

			// Otherwise, can't fix
			$this->handleFilesUploadSessionLookupError($error);
		}
	}

	/**
	 * Finish an upload session.
	 *
	 * @param array $args
	 *
	 * @return array Information on the uploaded file
	 */
	public function filesUploadSessionFinish($args)
	{
		$args['commit']['path'] = $this->formatPath($args['commit']['path']);

		try {
			return $this->request('files/upload_session/finish', $args, 'upload');
		} catch (BackWPup_Destination_Dropbox_API_Request_Exception $e) {
			$error = $e->getError();
			if ($error['.tag'] === 'lookup_failed') {
				if ($error['lookup_failed']['.tag'] === 'incorrect_offset') {
					$args['cursor']['offset'] = $error['lookup_failed']['correct_offset'];

					return $this->request('files/upload_session/finish', $args, 'upload');
				}
			}
			$this->handleFilesUploadSessionFinishError($e->getError());
		}
	}

	/**
	 * Starts an upload session.
	 *
	 * When a file larger than 150 MB needs to be uploaded, then this API
	 * endpoint is used to start a session to allow the file to be uploaded in
	 * chunks.
	 *
	 * @param array $args
	 *
	 * @return array    An array containing the session's ID.
	 */
	public function filesUploadSessionStart($args = [])
	{
		return $this->request('files/upload_session/start', $args, 'upload');
	}

	/**
	 * Get user's current account info.
	 *
	 * @return array
	 */
	public function usersGetCurrentAccount()
	{
		return $this->request('users/get_current_account');
	}

	/**
	 * Get quota info for this user.
	 *
	 * @return array
	 */
	public function usersGetSpaceUsage()
	{
		return $this->request('users/get_space_usage');
	}

	/**
	 * Get the user agent
	 *
	 * If no user agent has been provided, defaults to `BackWPup::get_plugin_data('User-Agent')`.
	 *
	 * @return string The user agent
	 */
	public function getUserAgent()
	{
		return $this->userAgent ?: \BackWPup::get_plugin_data('User-Agent');
	}

	/**
	 * Set the user agent
	 *
	 * @param string $userAgent
	 */
	public function setUserAgent($userAgent)
	{
		$this->userAgent = $userAgent;
	}

	/**
	 * Get the SSL ca-bundle path
	 *
	 * If no ca-bundle has been provided, defaults to `BackWPup::get_plugin_data('cacert')`.
	 *
	 * @return string The SSL ca-bundle
	 */
	public function getCaBundle()
	{
		return $this->caBundle ?: \BackWPup::get_plugin_data('cacert');
	}

	/**
	 * Set the path to the SSL ca-bundle
	 *
	 * @param string $caBundle The path to the ca-bundle file
	 */
	public function setCaBundle($caBundle)
	{
		$this->caBundle = $caBundle;
	}

	/**
	 * Set the job object
	 *
	 * @param BackWPup_Job $jobObject The job object to set
	 */
	public function setJobObject(BackWPup_Job $jobObject)
	{
		$this->jobObject = $jobObject;
	}

	/**
	 * Logs a message to the current job
	 *
	 * @param string $message The message to log
	 * @param int $level The log level
	 *
	 * @return bool|null True on success, null if no job object set
	 */
	protected function log($message, $level = E_USER_NOTICE)
	{
		if (!isset($this->jobObject)) {
			return null;
		}

		return $this->jobObject->log($message, $level);
	}

	/**
	 * Logs debug info about the current request
	 *
	 * @param string $endpoint The current request endpoint
	 * @param array $args The request args
	 *
	 * @return bool|null True on success, null if no job object set or debug is not enabled
	 */
	protected function logRequest($endpoint, array $args)
	{
		if (!isset($this->jobObject) || !$this->jobObject->is_debug()) {
			return null;
		}

		$message = "Call to $endpoint";

		if (isset($args['contents'])) {
			$message .= ' with ' . size_format(strlen($args['contents'])) . ' of data,';
			unset($args['contents']);
		}

		if (!empty($args)) {
			$message .= ' with parameters ' . json_encode($args);
		}

		return $this->log($message);
	}

	/**
	 * Request
	 *
	 * @param string $url
	 * @param array  $args
	 * @param string $endpointFormat
	 * @param bool   $echo
	 * @param string $bytes
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Exception
	 *
	 * @return array|mixed|string
	 */
	public function request($endpoint, $args = [], $endpointFormat = 'rpc', $echo = false, $bytes = null)
	{
		// Log request
		$this->logRequest($endpoint, $args);

		if ($bytes !== null) {
			$args['bytes'] = $bytes;
		}

		$request = $this->buildRequest($endpoint, $args, $endpointFormat);
		$client = $this->createClient();

		$response = $client->sendRequest($request);

		if ($response->getStatusCode() >= 500) {
			$this->handleServerException($response);
		} elseif ($response->getStatusCode() >= 400) {
			$this->handleRequestException($response);

			// If we're still here, then recurse
			return $this->request($endpoint, $args, $endpointFormat, $echo, $bytes);
		}

		if ($echo === true) {
			echo $response->getBody(); // phpcs:ignore
		}

		if ($response->getHeaderLine('Content-Type') === 'application/json') {
			return json_decode($response->getBody(), true);
		} else {
			return $response->getBody()->getContents();
		}
	}

	/**
	 * Gets the full URL to the Dropbox endpoint
	 *
	 * @param string $endpoint The API endpoint
	 * @param string $format The endpoint Format
	 *
	 * @return string The full URL
	 */
	private function getUrl($endpoint, $format)
	{
		switch ($format) {
			case 'oauth':
				$url = self::API_URL . $endpoint;
				break;

			case 'rpc':
				$url = self::API_URL . self::API_VERSION_URL . $endpoint;
				break;

			case 'upload':
			case 'download':
				$url = self::API_CONTENT_URL . self::API_VERSION_URL . $endpoint;
				break;
		}

		return $url;
	}

	/**
	 * Builds the options for the request
	 *
	 * @param string $endpoint The endpoint to call
	 * @param array $args The arguments for the request
	 * @param string $format The endpoint format
	 *
	 * @return \Psr\Http\Message\RequestInterface The HTTP request
	 */
	private function buildRequest($endpoint, array &$args, $format)
	{
		$url = $this->getUrl($endpoint, $format);

		$request = $this->createRequestFactory()
			->createRequest('POST', $url);

		if ($format !== 'oauth') {
			$request = new AuthorizationRequest($request);
			$request = $request->withOAuthToken($this->getTokens()['access_token']);
		}

		$streamFactory = new StreamFactory();

		switch ($format) {
			case 'oauth':
				$request = new AuthorizationRequest(new FormRequest($request, $streamFactory));
				$request = $request
					->withBasicAuth($this->oauthAppKey, $this->oauthAppSecret)
					->withFormParams($args)
					->withHeader('Accept', 'application/json');
				break;

			case 'rpc':
				$request = new JsonRequest($request, $streamFactory);
				$request = $request
					->withJsonData($args ?: null)
					->withHeader('Accept', 'application/json');
				break;

			case 'upload':
				if (isset($args['contents'])) {
					$stream = $streamFactory->createStream($args['contents']);
					$request = $request->withBody($stream);
					unset($args['contents']);
				}

				$request = $request
					->withHeader('Content-Type', 'application/octet-stream')
					->withHeader('Dropbox-API-Arg', json_encode($args, JSON_FORCE_OBJECT));
				break;

			case 'download':
				if (isset($args['bytes'])) {
					$request = $request
						->withHeader('Range', 'bytes=' . $args['bytes']);
					unset($args['bytes']);
				}

				$request = $request
					->withHeader('Content-Type', 'text/plain')
					->withHeader('Accept', 'application/octet-stream')
					->withHeader('Dropbox-API-Arg', json_encode($args, JSON_FORCE_OBJECT));
				break;
		}

		return $request;
	}

	/**
	 * Handle request exception
	 *
	 * Called for 4xx responses.
	 *
	 * @param \Psr\Http\Message\ResponseInterface $response The returned response
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Exception If the error cannot be handled
	 * @throws BackWPup_Destination_Dropbox_API_Request_Exception For endpoint-specific errors
	 *                                                            to be passed up the chain
	 */
	private function handleRequestException(ResponseInterface $response)
	{
		switch ($response->getStatusCode()) {
			case 400:
			case 401:
			case 403:
			case 409:
			case 429:
				$callback = [$this, 'handle' . $response->getStatusCode() . 'Error'];
				call_user_func($callback, $response);
				break;

			default:
				throw new BackWPup_Destination_Dropbox_API_Exception(
					sprintf(
						__(
							'(%1$s) An unknown error has occurred. Response from server: %2$s',
							'backwpup'
						),
						$response->getStatusCode(),
						$response->getBody()->getContents()
					)
				);
		}
	}

	/**
	 * Handle server exception
	 *
	 * Called for 5xx responses.
	 *
	 * @param \Psr\Http\Message\ResponseInterface $response The returned response
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Exception
	 */
	protected function handleServerException(ResponseInterface $response)
	{
			throw new BackWPup_Destination_Dropbox_API_Exception(
				sprintf(
					__(
						'(%1$d) An unexpected server error was encountered. Response from server: %2$s',
						'backwpup'
					),
					$response->getStatusCode(),
					$response->getBody()->getContents()
				)
			);
	}

	/**
	 * Handle 400 response error
	 *
	 * @param \Psr\Http\Message\ResponseInterface The returned response
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Exception
	 */
	protected function handle400Error(ResponseInterface $response)
	{
		throw new BackWPup_Destination_Dropbox_API_Exception(
			sprintf(
				__(
					'(400) Bad input parameter. Response from server: %s',
					'backwpup'
				),
				$response->getBody()->getContents()
			)
		);
	}

	/**
	 * Handle 401 response error
	 *
	 * @param \Psr\Http\Message\ResponseInterface The returned response
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Exception If token is invalid
	 */
	protected function handle401Error(ResponseInterface $response)
	{
		$error = json_decode($response->getBody()->getContents(), true);
		if ($error['error']['.tag'] === 'expired_access_token') {
			$this->refresh($this->oauthToken['refresh_token']);
		} else {
			throw new BackWPup_Destination_Dropbox_API_Exception(
				sprintf(
					__(
						'(401) Bad or expired token. Response from server: %s',
						'backwpup'
					),
					$error['error']['.tag']
				)
			);
		}
	}

	/**
	 * Handle 403 response error
	 *
	 * @param \Psr\Http\Message\ResponseInterface The returned response
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Exception
	 */
	protected function handle403Error(ResponseInterface $response)
	{
		$error = json_decode($response->getBody(), true);

		if ($error['error']['.tag'] === 'invalid_account_type') {
			// InvalidAccountTypeError
			if ($error['error']['invalid_account_type']['.tag'] === 'endpoint') {
				throw new BackWPup_Destination_Dropbox_API_Exception(
					__(
						'(403) You do not have permission to access this endpoint.',
						'backwpup'
					)
				);
			} elseif ($error['error']['invalid_account_type']['.tag'] === 'feature') {
				throw new BackWPup_Destination_Dropbox_API_Exception(
					__(
						'(403) You do not have permission to access this feature.',
						'backwpup'
					)
				);
			}
		}

		// Catch all
		throw new BackWPup_Destination_Dropbox_API_Exception(
			sprintf(
				__(
					'(403) You do not have permission to access this resource. Response from server: %s',
					'backwpup'
				),
				$error['error_summary']
			)
		);
	}

	/**
	 * Handle 409 response error
	 *
	 * @param \Psr\Http\Message\ResponseInterface The returned response
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Request_Exception
	 */
	protected function handle409Error(ResponseInterface $response)
	{
		$error = json_decode($response->getBody(), true);

		throw new BackWPup_Destination_Dropbox_API_Request_Exception(
			sprintf(
				__(
					'(409) Endpoint-specific error. Response from server: %s',
					'backwpup'
				),
				$error['error_summary']
			),
			$response->getStatusCode(),
			null,
			$error['error']
		);
	}

	/**
	 * Handle 429 response error
	 *
	 * This error is encountered when requests are being rate limited.
	 *
	 * @param \Psr\Http\Message\ResponseInterface The returned response
	 *
	 * @throws BackWPup_Destination_Dropbox_API_Exception If unable to detect time to wait
	 */
	protected function handle429Error(ResponseInterface $response)
	{
		if (!$response->hasHeader('Retry-After')) {
			throw new BackWPup_Destination_Dropbox_API_Exception(
				__(
					'(429) Requests are being rate limited. Please try again later.',
					'backwpup'
				)
			);
		} else {
			sleep(intval($response->getHeaderLine('Retry-After')));
		}
	}

	/**
	 * Creates a new HTTP client
	  *
	 * @return \Http\Client\HttpClient
	 */
	protected function createClient()
	{
		$options = [];
		if (empty($this->getCaBundle())) {
			$options['sslverify'] = false;
		} else {
			$options += [
				'sslverify' => true,
				'sslcertificates' => $this->getCaBundle(),
			];
		}

		if (!empty($this->getUserAgent())) {
			$options['user-agent'] = $this->getUserAgent();
		}

		return new WpHttpClient(new ResponseFactory(), new StreamFactory(), $options);
	}

	/**
	 * Creates a request factory for creating requests
	 *
	 * @return \Http\Message\RequestFactory
	 */
	protected function createRequestFactory()
	{
		return new RequestFactory();
	}

	/**
	 * Formats a path to be valid for Dropbox.
	 *
	 * @param string $path
	 *
	 * @return string The formatted path
	 */
	private function formatPath($path)
	{
		if (!empty($path) && substr($path, 0, 1) !== '/') {
			$path = '/' . rtrim($path, '/');
		} elseif ($path === '/') {
			$path = '';
		}

		return $path;
	}

	// Error Handlers

	private function handleFilesDeleteError($error)
	{
		switch ($error['.tag']) {
			case 'path_lookup':
				$this->handleFilesLookupError($error['path_lookup']);
				break;

			case 'path_write':
				$this->handleFilesWriteError($error['path_write']);
				break;

			case 'other':
				trigger_error('Could not delete file.', E_USER_WARNING);
				break;
		}
	}

	private function handleFilesGetMetadataError($error)
	{
		switch ($error['.tag']) {
			case 'path':
				$this->handleFilesLookupError($error['path']);
				break;

			case 'other':
				trigger_error('Cannot look up file metadata.', E_USER_WARNING);
				break;
		}
	}

	private function handleFilesGetTemporaryLinkError($error)
	{
		switch ($error['.tag']) {
			case 'path':
				$this->handleFilesLookupError($error['path']);
				break;

			case 'other':
				trigger_error('Cannot get temporary link.', E_USER_WARNING);
				break;
		}
	}

	private function handleFilesListFolderError($error)
	{
		switch ($error['.tag']) {
			case 'path':
				$this->handleFilesLookupError($error['path']);
				break;

			case 'other':
				trigger_error('Cannot list files in folder.', E_USER_WARNING);
				break;
		}
	}

	private function handleFilesListFolderContinueError($error)
	{
		switch ($error['.tag']) {
			case 'path':
				$this->handleFilesLookupError($error['path']);
				break;

			case 'reset':
				trigger_error('This cursor has been invalidated.', E_USER_WARNING);
				break;

			case 'other':
				trigger_error('Cannot list files in folder.', E_USER_WARNING);
				break;
		}
	}

	private function handleFilesLookupError($error)
	{
		switch ($error['.tag']) {
			case 'malformed_path':
				trigger_error('The path was malformed.', E_USER_WARNING);
				break;

			case 'not_found':
				trigger_error('File could not be found.', E_USER_WARNING);
				break;

			case 'not_file':
				trigger_error('That is not a file.', E_USER_WARNING);
				break;

			case 'not_folder':
				trigger_error('That is not a folder.', E_USER_WARNING);
				break;

			case 'restricted_content':
				trigger_error('This content is restricted.', E_USER_WARNING);
				break;

			case 'invalid_path_root':
				trigger_error('Path root is invalid.', E_USER_WARNING);
				break;

			case 'other':
				trigger_error('File could not be found.', E_USER_WARNING);
				break;
		}
	}

	private function handleFilesUploadSessionFinishError($error)
	{
		switch ($error['.tag']) {
			case 'lookup_failed':
				$this->handleFilesUploadSessionLookupError(
					$error['lookup_failed']
				);
				break;

			case 'path':
				$this->handleFilesWriteError($error['path']);
				break;

			case 'too_many_shared_folder_targets':
				trigger_error('Too many shared folder targets.', E_USER_WARNING);
				break;

			case 'other':
				trigger_error('The file could not be uploaded.', E_USER_WARNING);
				break;
		}
	}

	private function handleFilesUploadSessionLookupError($error)
	{
		switch ($error['.tag']) {
			case 'not_found':
				trigger_error('Session not found.', E_USER_WARNING);
				break;

			case 'incorrect_offset':
				trigger_error(
					'Incorrect offset given. Correct offset is ' .
					intval($error['correct_offset']) . '.',
					E_USER_WARNING
				);
				break;

			case 'closed':
				trigger_error(
					'This session has been closed already.',
					E_USER_WARNING
				);
				break;

			case 'not_closed':
				trigger_error('This session is not closed.', E_USER_WARNING);
				break;

			case 'other':
				trigger_error(
					'Could not look up the file session.',
					E_USER_WARNING
				);
				break;
		}
	}

	private function handleFilesUploadError($error)
	{
		switch ($error['.tag']) {
			case 'path':
				$this->handleFilesUploadWriteFailed($error['path']);
				break;

			case 'other':
				trigger_error('There was an unknown error when uploading the file.', E_USER_WARNING);
				break;
		}
	}

	private function handleFilesUploadWriteFailed($error)
	{
		$this->handleFilesWriteError($error['reason']);
	}

	private function handleFilesWriteError($error)
	{
		$message = '';

		// Type of error
		switch ($error['.tag']) {
			case 'malformed_path':
				$message = 'The path was malformed.';
				break;

			case 'conflict':
				$message = 'Cannot write to the target path due to conflict.';
				break;

			case 'no_write_permission':
				$message = 'You do not have permission to save to this location.';
				break;

			case 'insufficient_space':
				$message = 'You do not have enough space in your Dropbox.';
				break;

			case 'disallowed_name':
				$message = 'The given name is disallowed by Dropbox.';
				break;

			case 'team_folder':
				$message = 'Unable to modify team folders.';
				break;

			case 'other':
				$message = 'There was an unknown error when uploading the file.';
				break;
		}

		trigger_error($message, E_USER_WARNING); // phpcs:ignore
	}
}

Anon7 - 2022
AnonSec Team