Created
March 25, 2020 13:43
-
-
Save cuongdcdev/b2576d73f653baf4d801c3f1d033fe8c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Class for communicating with Dropbox API V2. | |
| */ | |
| final 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 $oauth_app_key = ''; | |
| /** | |
| * @var string | |
| */ | |
| private $oauth_app_secret = ''; | |
| /** | |
| * @var string | |
| */ | |
| private $oauth_token = ''; | |
| /** | |
| * Job object for logging. | |
| * | |
| * @var BackWPup_Job | |
| */ | |
| private $job_object; | |
| /** | |
| * @param string $boxtype | |
| * | |
| * @throws BackWPup_Destination_Dropbox_API_Exception | |
| */ | |
| public function __construct( $boxtype = 'dropbox', BackWPup_Job $job_object = null ) { | |
| if ( $boxtype === 'dropbox' ) { | |
| $this->oauth_app_key = get_site_option( | |
| 'backwpup_cfg_dropboxappkey', | |
| base64_decode( 'NXdtdXl0cm5qZzB5aHhw' ) | |
| ); | |
| $this->oauth_app_secret = BackWPup_Encryption::decrypt( | |
| get_site_option( 'backwpup_cfg_dropboxappsecret', base64_decode( 'cXYzZmp2N2IxcG1rbWxy' ) ) | |
| ); | |
| } else { | |
| $this->oauth_app_key = get_site_option( | |
| 'backwpup_cfg_dropboxsandboxappkey', | |
| base64_decode( 'a3RqeTJwdXFwZWVydW92' ) | |
| ); | |
| $this->oauth_app_secret = BackWPup_Encryption::decrypt( | |
| get_site_option( 'backwpup_cfg_dropboxsandboxappsecret', base64_decode( 'aXJ1eDF3Ym9mMHM5eGp6' ) ) | |
| ); | |
| } | |
| if ( empty( $this->oauth_app_key ) || empty( $this->oauth_app_secret ) ) { | |
| throw new BackWPup_Destination_Dropbox_API_Exception( "No App key or App Secret specified." ); | |
| } | |
| $this->job_object = $job_object; | |
| } | |
| /** | |
| * 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 = array(); | |
| $result = $this->filesListFolder( array( 'path' => $path ) ); | |
| if ( ! $result ) { | |
| return array(); | |
| } | |
| $files = array_merge( $files, $result['entries'] ); | |
| $args = array( '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( | |
| array( | |
| '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." | |
| ); | |
| } | |
| $chunk_size = 4194304*4; //4194304 = 4MB | |
| $file_handel = fopen( $file, 'rb' ); | |
| if ( ! $file_handel ) { | |
| throw new BackWPup_Destination_Dropbox_API_Exception( "Can not open source file for transfer." ); | |
| } | |
| if ( ! isset( $this->job_object->steps_data[ $this->job_object->step_working ]['uploadid'] ) ) { | |
| $this->job_object->log( __( 'Beginning new file upload session', 'backwpup' ) ); | |
| $session = $this->filesUploadSessionStart( | |
| ); | |
| $this->job_object->steps_data[ $this->job_object->step_working ]['uploadid'] = $session['session_id']; | |
| } | |
| if ( ! isset( $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] ) ) { | |
| $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] = 0; | |
| } | |
| if ( ! isset( $this->job_object->steps_data[ $this->job_object->step_working ]['totalread'] ) ) { | |
| $this->job_object->steps_data[ $this->job_object->step_working ]['totalread'] = 0; | |
| } | |
| //seek to current position | |
| if ( $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] > 0 ) { | |
| fseek( $file_handel, $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] ); | |
| } | |
| while ( $data = fread( $file_handel, $chunk_size ) ) { | |
| $chunk_upload_start = microtime( true ); | |
| if ( $this->job_object->is_debug() ) { | |
| $this->job_object->log( | |
| sprintf( __( 'Uploading %s of data', 'backwpup' ), size_format( strlen( $data ) ) ) | |
| ); | |
| } | |
| $this->filesUploadSessionAppendV2( | |
| array( | |
| 'contents' => $data, | |
| 'cursor' => array( | |
| 'session_id' => $this->job_object->steps_data[ $this->job_object->step_working ]['uploadid'], | |
| 'offset' => $this->job_object->steps_data[ $this->job_object->step_working ]['offset'], | |
| ), | |
| ) | |
| ); | |
| $chunk_upload_time = microtime( | |
| true | |
| ) - $chunk_upload_start; | |
| $this->job_object->steps_data[ $this->job_object->step_working ]['totalread'] += strlen( $data ); | |
| //args for next chunk | |
| $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] += $chunk_size; | |
| if ( $this->job_object->job['backuptype'] === 'archive' ) { | |
| $this->job_object->substeps_done = $this->job_object->steps_data[ $this->job_object->step_working ]['offset']; | |
| if ( strlen( $data ) == $chunk_size ) { | |
| $time_remaining = $this->job_object->do_restart_time(); | |
| //calc next chunk | |
| if ( $time_remaining < $chunk_upload_time ) { | |
| $chunk_size = floor( $chunk_size / $chunk_upload_time * ( $time_remaining - 3 ) ); | |
| if ( $chunk_size < 0 ) { | |
| $chunk_size = 1024; | |
| } | |
| if ( $chunk_size > 4194304*4 ) { | |
| $chunk_size = 4194304*4; | |
| } | |
| } | |
| } | |
| } | |
| $this->job_object->update_working_data(); | |
| //correct position | |
| fseek( $file_handel, $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] ); | |
| } | |
| fclose( $file_handel ); | |
| $this->job_object->log( | |
| sprintf( | |
| __( 'Finishing upload session with a total of %s uploaded', 'backwpup' ), | |
| size_format( $this->job_object->steps_data[ $this->job_object->step_working ]['totalread'] ) | |
| ) | |
| ); | |
| $response = $this->filesUploadSessionFinish( | |
| array( | |
| 'cursor' => array( | |
| 'session_id' => $this->job_object->steps_data[ $this->job_object->step_working ]['uploadid'], | |
| 'offset' => $this->job_object->steps_data[ $this->job_object->step_working ]['totalread'], | |
| ), | |
| 'commit' => array( | |
| 'path' => $path, | |
| 'mode' => ( $overwrite ) ? 'overwrite' : 'add', | |
| ), | |
| ) | |
| ); | |
| unset( $this->job_object->steps_data[ $this->job_object->step_working ]['uploadid'] ); | |
| unset( $this->job_object->steps_data[ $this->job_object->step_working ]['offset'] ); | |
| return $response; | |
| } | |
| /** | |
| * Set the oauth tokens for this request. | |
| * | |
| * @param $token | |
| * | |
| * @throws BackWPup_Destination_Dropbox_API_Exception | |
| */ | |
| public function setOAuthTokens( $token ) { | |
| if ( empty( $token['access_token'] ) ) { | |
| throw new BackWPup_Destination_Dropbox_API_Exception( "No oAuth token specified." ); | |
| } | |
| $this->oauth_token = $token; | |
| } | |
| /** | |
| * 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->oauth_app_key; | |
| } | |
| /** | |
| * Tkes the oauth code and returns the access token. | |
| * | |
| * @param string $code The oauth code | |
| * | |
| * @return array An array including the access token, account ID, and | |
| * other information. | |
| */ | |
| public function oAuthToken( $code ) { | |
| return $this->request( | |
| 'oauth2/token', | |
| array( | |
| 'code' => trim( $code ), | |
| 'grant_type' => 'authorization_code', | |
| 'client_id' => $this->oauth_app_key, | |
| 'client_secret' => $this->oauth_app_secret, | |
| ), | |
| 'oauth' | |
| ); | |
| } | |
| /** | |
| * Revokes the auth token. | |
| * | |
| * @return array | |
| */ | |
| public function authTokenRevoke() { | |
| return $this->request( 'auth/token/revoke' ); | |
| } | |
| /** | |
| * Download | |
| * | |
| * @throws BackWPup_Destination_Dropbox_API_Exception Because of a rate limit. | |
| * | |
| * @param array $args Argument for the api request. | |
| * | |
| * @return mixed Whatever the api request returns. | |
| */ | |
| public function download( $args, $start_byte = null, $end_byte = null ) { | |
| $args['path'] = $this->formatPath( $args['path'] ); | |
| if ( $start_byte !== null && $end_byte !== null ) { | |
| return $this->request( 'files/download', $args, 'download', false, "{$start_byte}-{$end_byte}" ); | |
| } | |
| 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 = array() ) { | |
| 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' ); | |
| } | |
| /** | |
| * Request | |
| * | |
| * @param $url | |
| * @param array $args | |
| * @param string $endpointFormat | |
| * @param string $data | |
| * @param bool $echo | |
| * @param string $bytes | |
| * | |
| * @throws BackWPup_Destination_Dropbox_API_Exception | |
| * | |
| * @return array|mixed|string | |
| */ | |
| private function request( $endpoint, $args = array(), $endpointFormat = 'rpc', $echo = false, $bytes = null ) { | |
| // Get complete URL | |
| switch ( $endpointFormat ) { | |
| 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; | |
| } | |
| if ( $this->job_object && $this->job_object->is_debug() && $endpointFormat != 'oauth' ) { | |
| $message = 'Call to ' . $endpoint; | |
| $parameters = $args; | |
| if ( isset( $parameters['contents'] ) ) { | |
| $message .= ', with ' . size_format( strlen( $parameters['contents'] ) ) . ' of data'; | |
| unset( $parameters['contents'] ); | |
| } | |
| if ( ! empty( $parameters ) ) { | |
| $message .= ', with parameters: ' . json_encode( $parameters ); | |
| } | |
| $this->job_object->log( $message ); | |
| } | |
| // Build cURL Request | |
| $ch = curl_init(); | |
| curl_setopt( $ch, CURLOPT_URL, $url ); | |
| curl_setopt( $ch, CURLOPT_POST, true ); | |
| $headers[] = 'Expect:'; | |
| if ( $endpointFormat !== 'oauth' ) { | |
| $headers[] = 'Authorization: Bearer ' . $this->oauth_token['access_token']; | |
| } | |
| switch ( $endpointFormat ) { | |
| case 'oauth': | |
| curl_setopt( $ch, CURLOPT_POSTFIELDS, http_build_query( $args, null, '&' ) ); | |
| $headers[] = 'Content-Type: application/x-www-form-urlencoded'; | |
| break; | |
| case 'rpc': | |
| $args = empty( $args ) ? null : $args; | |
| $headers[] = 'Content-Type: application/json'; | |
| curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode( $args ) ); | |
| break; | |
| case 'upload': | |
| $args['contents'] = isset( $args['contents'] ) ? $args['contents'] : ''; | |
| curl_setopt( $ch, CURLOPT_POSTFIELDS, $args['contents'] ); | |
| unset( $args['contents'] ); | |
| $headers[] = 'Content-Type: application/octet-stream'; | |
| $headers[] = empty( $args ) | |
| ? 'Dropbox-API-Arg: {}' | |
| : 'Dropbox-API-Arg: ' . json_encode( $args ); | |
| break; | |
| case 'download': | |
| curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true ); | |
| $headers[] = 'Content-Type: text/plain'; | |
| $headers[] = 'Dropbox-API-Arg: ' . json_encode( $args ); | |
| if ( $bytes !== null ) { | |
| $headers[] = "Range: bytes=$bytes"; | |
| } | |
| break; | |
| default: | |
| curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true ); | |
| $headers[] = 'Dropbox-API-Arg: ' . json_encode( $args ); | |
| break; | |
| } | |
| curl_setopt( $ch, CURLOPT_USERAGENT, BackWPup::get_plugin_data( 'User-Agent' ) ); | |
| curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); | |
| if ( BackWPup::get_plugin_data( 'cacert' ) ) { | |
| curl_setopt( $ch, CURLOPT_SSLVERSION, 1 ); | |
| curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, true ); | |
| $curl_version = curl_version(); | |
| if ( strstr( $curl_version['ssl_version'], 'NSS/' ) === false ) { | |
| curl_setopt( | |
| $ch, | |
| CURLOPT_SSL_CIPHER_LIST, | |
| 'ECDHE-RSA-AES256-GCM-SHA384:' . | |
| 'ECDHE-RSA-AES128-GCM-SHA256:' . | |
| 'ECDHE-RSA-AES256-SHA384:' . | |
| 'ECDHE-RSA-AES128-SHA256:' . | |
| 'ECDHE-RSA-AES256-SHA:' . | |
| 'ECDHE-RSA-AES128-SHA:' . | |
| 'ECDHE-RSA-RC4-SHA:' . | |
| 'DHE-RSA-AES256-GCM-SHA384:' . | |
| 'DHE-RSA-AES128-GCM-SHA256:' . | |
| 'DHE-RSA-AES256-SHA256:' . | |
| 'DHE-RSA-AES128-SHA256:' . | |
| 'DHE-RSA-AES256-SHA:' . | |
| 'DHE-RSA-AES128-SHA:' . | |
| 'AES256-GCM-SHA384:' . | |
| 'AES128-GCM-SHA256:' . | |
| 'AES256-SHA256:' . | |
| 'AES128-SHA256:' . | |
| 'AES256-SHA:' . | |
| 'AES128-SHA' | |
| ); | |
| } | |
| if ( defined( 'CURLOPT_PROTOCOLS' ) ) { | |
| curl_setopt( $ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS ); | |
| } | |
| if ( defined( 'CURLOPT_REDIR_PROTOCOLS' ) ) { | |
| curl_setopt( $ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS ); | |
| } | |
| curl_setopt( $ch, CURLOPT_CAINFO, BackWPup::get_plugin_data( 'cacert' ) ); | |
| curl_setopt( $ch, CURLOPT_CAPATH, dirname( BackWPup::get_plugin_data( 'cacert' ) ) ); | |
| } else { | |
| curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false ); | |
| } | |
| curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers ); | |
| $output = ''; | |
| if ( $echo ) { | |
| echo curl_exec( $ch ); | |
| } else { | |
| curl_setopt( $ch, CURLOPT_HEADER, true ); | |
| $response = curl_exec( $ch ); | |
| if ( stripos( $response, "HTTP/1.0 200 Connection established\r\n\r\n" ) !== false ) { | |
| $response = str_ireplace( "HTTP/1.0 200 Connection established\r\n\r\n", '', $response ); | |
| } | |
| $response = explode( "\r\n\r\n", $response, 2 ); | |
| if ( ! empty( $response[1] ) ) { | |
| $output = json_decode( $response[1], true ); | |
| } | |
| } | |
| $status = curl_getinfo( $ch ); | |
| // Handle error codes | |
| // If 409 (endpoint-specific error), let the calling method handle it | |
| // Code 429 = rate limited | |
| if ( $status['http_code'] == 429 ) { | |
| $wait = 0; | |
| if ( preg_match( "/retry-after:\s*(.*?)\r/i", $response[0], $matches ) ) { | |
| $wait = trim( $matches[1] ); | |
| } | |
| //only wait if we get a retry-after header. | |
| if ( ! empty( $wait ) ) { | |
| trigger_error( | |
| sprintf( | |
| '(429) Your app is making too many requests and is being rate limited. Error 429 can be triggered on a per-app or per-user basis. Wait for %d seconds.', | |
| $wait | |
| ), | |
| E_USER_WARNING | |
| ); | |
| sleep( $wait ); | |
| } else { | |
| throw new BackWPup_Destination_Dropbox_API_Exception( | |
| '(429) This indicates a transient server error.' | |
| ); | |
| } | |
| //redo request | |
| return $this->request( $url, $args, $endpointFormat, $endpointFormat, $echo ); | |
| } // We can't really handle anything else, so throw it back to the caller | |
| elseif ( isset( $output['error'] ) || $status['http_code'] >= 400 || curl_errno( $ch ) > 0 ) { | |
| $code = $status['http_code']; | |
| if ( curl_errno( $ch ) != 0 ) { | |
| $message = '(' . curl_errno( $ch ) . ') ' . curl_error( $ch ); | |
| $code = 0; | |
| } elseif ( $status['http_code'] == 400 ) { | |
| $message = '(400) Bad input parameter: ' . strip_tags( $response[1] ); | |
| } elseif ( $status['http_code'] == 401 ) { | |
| $message = '(401) Bad or expired token. This can happen if the user or Dropbox revoked or expired an access token. To fix, you should re-authenticate the user.'; | |
| } elseif ( $status['http_code'] == 409 ) { | |
| $message = $output['error_summary']; | |
| } elseif ( $status['http_code'] >= 500 ) { | |
| $message = '(' . $status['http_code'] . ') There is an error on the Dropbox server.'; | |
| } else { | |
| $message = '(' . $status['http_code'] . ') Invalid response.'; | |
| } | |
| if ( $this->job_object && $this->job_object->is_debug() ) { | |
| $this->job_object->log( 'Response with header: ' . $response[0] ); | |
| } | |
| throw new BackWPup_Destination_Dropbox_API_Request_Exception( | |
| $message, | |
| $code, | |
| null, | |
| isset($output['error']) ? $output['error'] : ['.tag' => 'other'] | |
| ); | |
| } else { | |
| curl_close( $ch ); | |
| if ( ! is_array( $output ) ) { | |
| return $response[1]; | |
| } else { | |
| return $output; | |
| } | |
| } | |
| } | |
| /** | |
| * 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 ' . | |
| $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 ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment