269 lines
8.0 KiB
PHP
269 lines
8.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Exceptions\DropboxRetryException;
|
|
use App\Storage;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class DropboxService
|
|
{
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $accessToken;
|
|
|
|
/**
|
|
* Configuration related to the Backblaze B2 service.
|
|
* @var \Illuminate\Config\Repository|mixed
|
|
*/
|
|
private $config;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->config = config('services.dropbox');
|
|
}
|
|
|
|
public function authoriseUrl(Storage $storage)
|
|
{
|
|
$service = $storage->externalService;
|
|
$redirectUrl = $this->callbackUrl();
|
|
|
|
return sprintf(
|
|
'%s?client_id=%s&response_type=code&redirect_uri=%s&state=%s',
|
|
$this->config['authorise_url'],
|
|
urlencode(decrypt($service->app_id)),
|
|
urlencode($redirectUrl),
|
|
urlencode(encrypt($storage->id))
|
|
);
|
|
}
|
|
|
|
public function callbackUrl()
|
|
{
|
|
return route('services.authoriseDropbox');
|
|
}
|
|
|
|
public function deleteFile($pathOnStorage)
|
|
{
|
|
$dropboxData = ['path' => $pathOnStorage];
|
|
|
|
$deleteResult = $this->sendRequest(
|
|
$this->config['delete_url'],
|
|
'POST',
|
|
$dropboxData,
|
|
[
|
|
'http_headers' => [
|
|
'Content-Type: application/json'
|
|
],
|
|
'post_body_is_json' => true
|
|
]
|
|
);
|
|
|
|
Log::debug('DropboxService - response to deleteFile.', ['response' => $deleteResult, 'path' => $pathOnStorage]);
|
|
}
|
|
|
|
public function downloadFile($pathOnStorage)
|
|
{
|
|
$dropboxArgs = ['path' => $pathOnStorage];
|
|
|
|
return $this->sendRequest(
|
|
$this->config['download_url'],
|
|
'POST',
|
|
null,
|
|
[
|
|
'http_headers' => [
|
|
sprintf('Dropbox-API-Arg: %s', json_encode($dropboxArgs)),
|
|
'Content-Type: application/octet-stream'
|
|
],
|
|
'post_body_is_json' => false,
|
|
'response_body_is_json' => false
|
|
]
|
|
);
|
|
}
|
|
|
|
public function handleAuthenticationResponse(Request $request, Storage $storage)
|
|
{
|
|
$authorisationCode = $request->query('code');
|
|
|
|
$storage->access_token = encrypt($this->convertAuthorisationCodeToToken($authorisationCode, $storage));
|
|
$storage->save();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param string $accessToken
|
|
*/
|
|
public function setAccessToken(string $accessToken)
|
|
{
|
|
$this->accessToken = $accessToken;
|
|
}
|
|
|
|
public function uploadFile($pathToFileToUpload, $pathOnStorage)
|
|
{
|
|
$dropboxArgs = [
|
|
'path' => $pathOnStorage,
|
|
'mode' => 'overwrite',
|
|
'mute' => true
|
|
];
|
|
|
|
$shouldRetry = true;
|
|
while ($shouldRetry)
|
|
{
|
|
try
|
|
{
|
|
$uploadResult = $this->sendRequest(
|
|
$this->config['upload_url'],
|
|
'POST',
|
|
file_get_contents($pathToFileToUpload),
|
|
[
|
|
'http_headers' => [
|
|
sprintf('Dropbox-API-Arg: %s', json_encode($dropboxArgs)),
|
|
'Content-Type: application/octet-stream'
|
|
],
|
|
'post_body_is_json' => false
|
|
]
|
|
);
|
|
|
|
$shouldRetry = false;
|
|
Log::debug('DropboxService - response to uploadFile.', ['response' => $uploadResult, 'path' => $pathOnStorage]);
|
|
}
|
|
catch (DropboxRetryException $dre)
|
|
{
|
|
// Retry - leave shouldRetry as true
|
|
Log::debug('DropboxService - Dropbox reported a lock/rate limit and requested to retry');
|
|
sleep(2);
|
|
}
|
|
catch (\Exception $ex)
|
|
{
|
|
$shouldRetry = false;
|
|
Log::debug('DropboxService - exception in uploadFile.', ['exception' => $ex->getMessage()]);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function convertAuthorisationCodeToToken($authorisationCode, Storage $storage)
|
|
{
|
|
$service = $storage->externalService;
|
|
$credentials = sprintf('%s:%s', decrypt($service->app_id), decrypt($service->app_secret));
|
|
$redirectUrl = $this->callbackUrl();
|
|
|
|
$httpHeaders = [
|
|
'Accept: application/json',
|
|
sprintf('Authorization: Basic %s', base64_encode($credentials))
|
|
];
|
|
|
|
$ch = curl_init($this->config['token_url']);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, [
|
|
'code' => $authorisationCode,
|
|
'grant_type' => 'authorization_code',
|
|
'redirect_uri' => $redirectUrl
|
|
]);
|
|
|
|
$response = json_decode(curl_exec($ch));
|
|
if (is_null($response) || $response === false)
|
|
{
|
|
throw new \Exception('Unable to read the response from Dropbox');
|
|
}
|
|
else if (isset($response->error_description))
|
|
{
|
|
throw new \Exception(sprintf('Error from Dropbox: %s', $response->error_description));
|
|
}
|
|
|
|
return $response->access_token;
|
|
}
|
|
|
|
private function getBasicHttpClient($url, $method = 'GET', array $httpHeaders = [])
|
|
{
|
|
$httpHeaders = array_merge(
|
|
[
|
|
'Accept: application/json',
|
|
sprintf('Authorization: Bearer %s', $this->accessToken)
|
|
],
|
|
$httpHeaders
|
|
);
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
|
|
switch (strtoupper($method))
|
|
{
|
|
case 'GET':
|
|
curl_setopt($ch, CURLOPT_HTTPGET, true);
|
|
break;
|
|
|
|
case 'POST':
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
break;
|
|
}
|
|
|
|
return $ch;
|
|
}
|
|
|
|
private function sendRequest($url, $method = 'GET', $postData = null, array $postOptions = [])
|
|
{
|
|
$postOptions = array_merge(
|
|
[
|
|
'http_headers' => [],
|
|
'post_body_is_json' => true,
|
|
'response_body_is_json' => true
|
|
],
|
|
$postOptions
|
|
);
|
|
$httpHeaders = $postOptions['http_headers'];
|
|
|
|
$ch = $this->getBasicHttpClient($url, $method, $httpHeaders);
|
|
|
|
Log::info(sprintf('DropboxService - %s: %s', strtoupper($method), $url));
|
|
Log::debug('DropboxService - HTTP headers:', $httpHeaders);
|
|
|
|
if (!is_null($postData))
|
|
{
|
|
if ($postOptions['post_body_is_json'])
|
|
{
|
|
// Only log a post body if we have one and it's in JSON format (i.e. not a file upload)
|
|
Log::debug('DropboxService - Body: ', $postData);
|
|
$postData = json_encode($postData);
|
|
}
|
|
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
|
|
}
|
|
|
|
$result = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
|
|
Log::info(sprintf('DropboxService - Received HTTP code %d', $httpCode));
|
|
|
|
// Only log a result if we have one and it's in JSON format (i.e. not a file download)
|
|
if (!is_null($result) && $result !== false && $postOptions['response_body_is_json'])
|
|
{
|
|
Log::debug($result);
|
|
}
|
|
|
|
try
|
|
{
|
|
if ($httpCode != 200 && $httpCode != 304)
|
|
{
|
|
if ($httpCode == 429)
|
|
{
|
|
throw new DropboxRetryException($httpCode, new \Exception(sprintf('Exception from Dropbox: %s', $result)));
|
|
}
|
|
|
|
throw new \Exception(sprintf('Exception from Dropbox: %s', $result));
|
|
}
|
|
|
|
return $postOptions['response_body_is_json']
|
|
? json_decode($result)
|
|
: $result;
|
|
}
|
|
finally
|
|
{
|
|
curl_close($ch);
|
|
}
|
|
}
|
|
} |