Sindbad~EG File Manager
<?php
// @codingStandardsIgnoreFile
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed');
class UpdraftPlus_Backblaze_CurlClient {
protected $accountId;
protected $applicationKey;
protected $credentials;
protected $authToken;
protected $apiUrl;
protected $downloadUrl;
// Whether to verify the server certificate or not
protected $sslVerify = true;
// If set to a string, then it indicates a path for a CA store to use
protected $useCACerts = false;
protected $singleBucketKeyId;
/**
* Constructor
*
* @param String $accountId
* @param String $applicationKey
* @param String $singleBucketKeyId
* @param Array $options
*/
public function __construct($accountId, $applicationKey, $singleBucketKeyId = '', array $options = array()) {
$this->accountId = $accountId;
$this->applicationKey = $applicationKey;
$this->singleBucketKeyId = $singleBucketKeyId;
if (isset($options['ssl_verify'])) $this->sslVerify = $options['ssl_verify'];
if (isset($options['ssl_ca_certs'])) $this->useCACerts = $options['ssl_ca_certs'];
$this->authorizeAccount();
}
protected function request($method = 'GET', $uri = '', array $options = array(), $as_json = true) {
$session = curl_init($uri);
$headers = array();
if (isset($options['auth'])) {
$account_id = empty($this->singleBucketKeyId) ? $this->accountId : $this->singleBucketKeyId;
$headers[] = 'Authorization: Basic ' . base64_encode($account_id . ':' . $this->applicationKey);
}
if (isset($options['headers'])) {
foreach ($options['headers'] as $key => $header) {
$headers[] = $key . ': ' . $header;
}
}
if ($this->sslVerify) {
curl_setopt($session, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($session, CURLOPT_SSL_VERIFYHOST, 2);
} else {
curl_setopt($session, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($session, CURLOPT_SSL_VERIFYHOST, 0);
}
if ($this->useCACerts) {
curl_setopt($session, CURLOPT_CAINFO, $this->useCACerts);
}
if ('GET' == $method) {
curl_setopt($session, CURLOPT_HTTPGET, true);
} else {
$data = array();
if (isset($options['json'])) {
$headers[] = "Accept: application/json";
$data = json_encode($options['json']);
}
if (isset($options['body'])) {
$data = $options['body'];
}
curl_setopt($session, CURLOPT_POSTFIELDS, $data);
curl_setopt($session, CURLOPT_POST, true);
}
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
if (isset($options['sink'])) {
$sink = fopen($options['sink'], 'w+');
curl_setopt($session, CURLOPT_FILE, $sink);
curl_setopt($session, CURLOPT_FOLLOWLOCATION, true);
}
if (isset($options['session'])) {
return $session;
}
$response = curl_exec($session);
if (0 != ($curl_error = curl_errno($session))) {
throw new Exception("Curl error ($curl_error): ".curl_error($session), $curl_error);
}
$decode_response = json_decode($response, true);
if (isset($decode_response['status']) && 200 !== $decode_response['status']) {
throw new Exception($decode_response['message'], $decode_response['status']);
}
curl_close($session);
if (!empty($sink)) @fclose($sink);
if ($as_json) return $decode_response;
return $response;
}
public function uploadLargeStart($options) {
// Request body parameters
if ('/' === substr($options['FileName'], 0, 1)) {
$options['FileName'] = ltrim($options['FileName'], '/');
}
if (!isset($options['BucketId']) && isset($options['BucketName'])) {
$options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
}
if (!isset($options['FileContentType'])) {
$options['FileContentType'] = 'b2/x-auto';
}
// Request start large file upload
$response = $this->request('POST', $this->apiUrl . '/b2_start_large_file', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'bucketId' => $options['BucketId'],
'fileName' => $options['FileName'],
'contentType' => $options['FileContentType'],
),
));
/*
* fileId
* fileName
* accountId
* bucketId
* contentType
* fileInfo
* uploadTimestamp
*/
return $response;
}
public function uploadLargeUrl($options) {
if (!isset($options['FileId'])) {
throw new Exception('FileId required');
}
$response = $this->request('POST', $this->apiUrl . '/b2_get_upload_part_url', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'fileId' => $options['FileId'],
),
));
/*
* authorizationToken
* fileId
* uploadUrl
*/
return $response;
}
public function uploadLargePart($options) {
if (!isset($options['AuthorizationToken'])) {
throw new Exception('AuthorizationToken required');
}
if (!isset($options['FilePartNo'])) {
throw new Exception('FilePartNo required');
}
if (!isset($options['UploadUrl'])) {
throw new Exception('UploadUrl required');
}
if (!isset($options['Body'])) {
throw new Exception('Body required');
}
if (is_resource($options['Body'])) {
// We need to calculate the file's hash incrementally from the stream.
$context = hash_init('sha1');
hash_update_stream($context, $options['Body']);
$hash = hash_final($context);
// Similarly, we have to use fstat to get the size of the stream.
$fstat = fstat($options['Body']);
$size = $fstat['size'];
// Rewind the stream before passing it to the HTTP client.
rewind($options['Body']);
} else {
// We've been given a simple string body, it's super simple to calculate the hash and size.
$hash = sha1($options['Body']);
$size = mb_strlen($options['Body'], '8bit');
}
$response = $this->request('POST', $options['UploadUrl'], array(
'headers' => array(
'Authorization' => $options['AuthorizationToken'],
'X-Bz-Part-Number' => $options['FilePartNo'],
'Content-Length' => $size,
'X-Bz-Content-Sha1' => $hash,
),
'body' => $options['Body'],
));
/*
* fileId
* partNumber
* contentLength
* contentSha1
*/
return $response;
}
public function uploadLargeFinish($options) {
if (!isset($options['FileId'])) {
throw new Exception('FileId required');
}
if (!isset($options['FilePartSha1Array'])) {
throw new Exception('FilePartSha1Array required');
}
if (!is_array($options['FilePartSha1Array'])) {
throw new Exception("FilePartSha1Array must be an array");
}
$response = $this->request('POST', $this->apiUrl . '/b2_finish_large_file', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'fileId' => (string) $options['FileId'],
'partSha1Array' => $options['FilePartSha1Array'],
),
));
if (empty($response['contentLength'])) {
throw new Exception('B2: uploadLargeFinish error: contentLength returned was empty ('.serialize($response).')');
}
return new UpdraftPlus_Backblaze_File(
$response['fileId'],
$response['fileName'],
$response['contentSha1'],
$response['contentLength'],
$response['contentType'],
$response['fileInfo']
);
}
protected function authorizeAccount() {
$response = $this->request("GET", 'https://api.backblazeb2.com/b2api/v1/b2_authorize_account', array(
'auth' => array($this->accountId, $this->applicationKey),
));
$this->authToken = $response['authorizationToken'];
$this->apiUrl = $response['apiUrl'] . '/b2api/v1';
$this->downloadUrl = $response['downloadUrl'];
}
public function listBuckets() {
$buckets = array();
$response = $this->request('POST', $this->apiUrl . '/b2_list_buckets', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'accountId' => $this->accountId,
),
));
if (!isset($response['buckets'])) throw new Exception('Failed to list buckets: '.serialize($response));
foreach ($response['buckets'] as $bucket) {
$is_file_lock_enabled = isset($bucket['fileLockConfiguration']['value']['isFileLockEnabled']) ? $bucket['fileLockConfiguration']['value']['isFileLockEnabled'] : false;
$buckets[] = new UpdraftPlus_Backblaze_Bucket($bucket['bucketId'], $bucket['bucketName'], $bucket['bucketType'], $is_file_lock_enabled);
}
return $buckets;
}
protected function getBucketIdFromName($name) {
$buckets = $this->listBuckets();
$name = strtolower($name);
foreach ($buckets as $bucket) {
if ($bucket->getName() === $name) {
return $bucket->getId();
}
}
return null;
}
protected function getBucketNameFromId($id) {
$buckets = $this->listBuckets();
foreach ($buckets as $bucket) {
if ($bucket->getId() === $id) {
return $bucket->getName();
}
}
return null;
}
protected function getFileIdFromBucketAndFileName($bucketName, $fileName) {
$files = $this->listFiles(array(
'BucketName' => $bucketName,
'FileName' => $fileName,
));
foreach ($files as $file) {
if ($file->getName() === $fileName) {
return $file->getId();
}
}
return null;
}
public function listFiles($options) {
// if FileName is set, we only attempt to retrieve information about that single file.
$fileName = !empty($options['FileName']) ? $options['FileName'] : null;
$nextFileName = null;
$maxFileCount = 1000;
$files = array();
if (!isset($options['BucketId']) && isset($options['BucketName'])) {
$options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
}
if ($fileName) {
$nextFileName = $fileName;
$maxFileCount = 1;
}
$json = array(
'bucketId' => $options['BucketId'],
'startFileName' => $nextFileName,
'maxFileCount' => $maxFileCount,
);
if (!empty($options['Prefix'])) $json['prefix'] = $options['Prefix'];
// B2 returns, at most, 1000 files per "page". Loop through the pages and compile an array of File objects.
while (true) {
$response = $this->request('POST', $this->apiUrl . '/b2_list_file_names', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => $json
));
if (!isset($response['files'])) throw new Exception('Failed to list files. '.serialize($files));
foreach ($response['files'] as $file) {
// if we have a file name set, only retrieve information if the file name matches
if (!$fileName || ($fileName === $file['fileName'])) {
$files[] = new UpdraftPlus_Backblaze_File($file['fileId'], $file['fileName'], null, $file['size']);
}
}
if ($fileName || $response['nextFileName'] === null) {
// We've got all the files - break out of loop.
break;
}
$json['startFileName'] = $response['nextFileName'];
}
return $files;
}
public function upload($options) {
// Clean the path if it starts with /.
if (substr($options['FileName'], 0, 1) === '/') {
$options['FileName'] = ltrim($options['FileName'], '/');
}
if (!isset($options['BucketId']) && isset($options['BucketName'])) {
$options['BucketId'] = $this->getBucketIdFromName($options['BucketName']);
}
// Retrieve the URL that we should be uploading to.
$response = $this->request('POST', $this->apiUrl . '/b2_get_upload_url', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'bucketId' => $options['BucketId'],
),
));
$uploadEndpoint = $response['uploadUrl'];
$uploadAuthToken = $response['authorizationToken'];
if (is_resource($options['Body'])) {
// We need to calculate the file's hash incrementally from the stream.
$context = hash_init('sha1');
hash_update_stream($context, $options['Body']);
$hash = hash_final($context);
// Similarly, we have to use fstat to get the size of the stream.
$fstat = fstat($options['Body']);
$size = $fstat['size'];
// Rewind the stream before passing it to the HTTP client.
rewind($options['Body']);
} else {
// We've been given a simple string body, it's super simple to calculate the hash and size.
$hash = sha1($options['Body']);
$size = mb_strlen($options['Body'], '8bit');
}
if (!isset($options['FileLastModified'])) {
$options['FileLastModified'] = round(microtime(true) * 1000);
}
if (!isset($options['FileContentType'])) {
$options['FileContentType'] = 'b2/x-auto';
}
$response = $this->request('POST', $uploadEndpoint, array(
'headers' => array(
'Authorization' => $uploadAuthToken,
'Content-Type' => $options['FileContentType'],
'Content-Length' => $size,
'X-Bz-File-Name' => $options['FileName'],
'X-Bz-Content-Sha1' => $hash,
'X-Bz-Info-src_last_modified_millis' => $options['FileLastModified'],
),
'body' => $options['Body'],
));
return new UpdraftPlus_Backblaze_File(
$response['fileId'],
$response['fileName'],
$response['contentSha1'],
$response['contentLength'],
$response['contentType'],
$response['fileInfo']
);
}
public function download($options) {
$requestUrl = null;
$requestOptions = array(
'headers' => array(
'Authorization' => $this->authToken,
),
'sink' => isset($options['SaveAs']) ? $options['SaveAs'] : null,
);
if (isset($options['FileId'])) {
$requestOptions['query'] = array('fileId' => $options['FileId']);
$requestUrl = $this->downloadUrl . '/b2api/v1/b2_download_file_by_id';
} else {
if (!isset($options['BucketName']) && isset($options['BucketId'])) {
$options['BucketName'] = $this->getBucketNameFromId($options['BucketId']);
}
$requestUrl = sprintf('%s/file/%s/%s', $this->downloadUrl, $options['BucketName'], $options['FileName']);
}
if (isset($options['headers'])) {
$requestOptions['headers'] = array_merge($requestOptions['headers'], $options['headers']);
}
$response = $this->request('GET', $requestUrl, $requestOptions, false);
return isset($options['SaveAs']) ? true : $response;
}
public function getFile($options) {
if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
$options['FileId'] = $this->getFileIdFromBucketAndFileName($options['BucketName'], $options['FileName']);
if (!$options['FileId']) {
throw new UpdraftPlus_Backblaze_NotFoundException();
}
}
$response = $this->request('POST', $this->apiUrl . '/b2_get_file_info', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'fileId' => $options['FileId'],
),
));
return new UpdraftPlus_Backblaze_File(
$response['fileId'],
$response['fileName'],
$response['contentSha1'],
$response['contentLength'],
$response['contentType'],
$response['fileInfo'],
$response['bucketId'],
$response['action'],
$response['uploadTimestamp']
);
}
/**
* Delete a file
*
* @param Array $options - possible keys are FileName, FileId, BucketName
*
* @return Boolean. Can also throw an exception; including UpdraftPlus_Backblaze_NotFoundException if the file was not found.
*/
public function deleteFile($options) {
if (!isset($options['FileName'])) {
$file = $this->getFile($options);
$options['FileName'] = $file->getName();
}
if (!isset($options['FileId']) && isset($options['BucketName']) && isset($options['FileName'])) {
$file = $this->getFile($options);
$options['FileId'] = $file->getId();
}
$delete_result = $this->request('POST', $this->apiUrl . '/b2_delete_file_version', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'fileName' => $options['FileName'],
'fileId' => $options['FileId'],
),
));
return (is_array($delete_result) && !empty($delete_result['fileId'])) ? true : false;
}
/**
* Delete multiple files
*
* @param Array $files_to_delete - array of possible files to delete; sub-keys are FileName, FileId, BucketName
* @param String $bucket_name - the bucket that files are being deleted from
* @param String|Null - path prefix (to prevent unnecessary scanning of other paths)
*
* @return Array|Boolean
*/
public function deleteMultipleFiles($files_to_delete, $bucket_name, $path_prefix = null) {
if (count($files_to_delete) == 0) {
return false;
}
$active = null;
$sessions = [];
$result = [];
$bulk_session = curl_multi_init();
$list_options = array(
'BucketName' => $bucket_name
);
if (is_string($path_prefix) && '' !== $path_prefix) $list_options['Prefix'] = $path_prefix;
$files = $this->listFiles($list_options);
$files_lookup = array();
foreach ($files as $file_object) {
$file_name = $file_object->getName();
$file_id = $file_object->getId();
$files_lookup[$file_name] = $file_id;
}
foreach ($files_to_delete as $file_identification) {
try {
if (!isset($file_identification['FileName'])) {
// We should not enter here as we always pass a file name but just in case
$file = $this->getFile($file_identification);
$file_identification['FileName'] = $file->getName();
$file_identification['FileId'] = $file->getId();
} elseif (!isset($file_identification['FileId'])) {
if (isset($files_lookup[$file_identification['FileName']])) {
$file_identification['FileId'] = $files_lookup[$file_identification['FileName']];
} else {
// We should not enter here as all the files should be in the same bucket but just in case
$file = $this->getFile($file_identification);
$file_identification['FileId'] = $file->getId();
}
}
} catch (UpdraftPlus_Backblaze_NotFoundException $e) {
array_push($sessions, true);
continue;
}
$session = $this->request('POST', $this->apiUrl . '/b2_delete_file_version', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'fileName' => $file_identification['FileName'],
'fileId' => $file_identification['FileId'],
),
'session' => true
));
array_push($sessions, $session);
curl_multi_add_handle($bulk_session, $session);
}
do {
$status = curl_multi_exec($bulk_session, $active);
if ($active) {
curl_multi_select($bulk_session);
}
} while ($active && $status == CURLM_OK);
foreach ($sessions as $session) {
if (is_bool($session)) {
array_push($result, $session);
continue;
}
$response = curl_multi_getcontent($session);
array_push($result, $response);
curl_multi_remove_handle($bulk_session, $session);
}
curl_multi_close($bulk_session);
return (is_array($result) && !empty($result)) ? $result : false;
}
/**
* Create a private bucket with the given name.
*
* @param String $bucket_name - valid bucket name
* @throws Exception
*
* @return boolean - If bucket created successfully, it returns true otherwise false.
*/
public function createPrivateBucket($bucket_name) {
try {
$response = $this->request('POST', $this->apiUrl.'/b2_create_bucket',
array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'accountId' => $this->accountId,
'bucketName' => $bucket_name,
'bucketType' => 'allPrivate'
)
)
);
} catch (Exception $e) {
if (400 == $e->getCode()) {
throw new Exception("Bucket can't be created because Bucket name is already in use.", $e->getCode());
} else {
throw $e;
}
return false;
}
if (isset($response['bucketId']) && isset($response['bucketName']) && isset($response['bucketType'])) {
return true;
}
return false;
}
/**
* Set object lock for a file in a B2 storage bucket.
*
* @param string $id The ID of the file to set object lock for.
* @param string $filename The name of the file.
* @param int $object_lock_duration The duration (in days) for which to set object lock.
*
* @return array $response The array of response.
*/
public function setObjectLock($id, $filename, $object_lock_duration) {
// Calculate the retain until timestamp (milliseconds) based on the $object_lock_duration.
$retain_until_timestamp = (time() + ($object_lock_duration * 86400)) * 1000;
// Make a request to the B2 API to update file retention settings.
$response = $this->request('POST', $this->apiUrl.'/b2_update_file_retention', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'fileId' => $id,
'fileName' => $filename,
'fileRetention' => array(
'mode' => 'compliance',
'retainUntilTimestamp' => $retain_until_timestamp
)
)
));
return $response;
}
/**
* Set object lock for a B2 storage bucket.
*
* @param string $id The ID of the bucket to set object lock for.
*
* @return array $response The array of response.
*/
public function setObjectLockToBucket($id) {
// Make a request to the B2 API to update file retention settings.
$response = $this->request('POST', $this->apiUrl.'/b2_update_bucket', array(
'headers' => array(
'Authorization' => $this->authToken,
),
'json' => array(
'accountId' => $this->accountId,
'bucketId' => $id,
'fileLockEnabled' => true
)
));
return $response;
}
}
final class UpdraftPlus_Backblaze_Bucket {
const TYPE_PUBLIC = 'allPublic';
const TYPE_PRIVATE = 'allPrivate';
protected $id;
protected $name;
protected $type;
protected $objectLockEnabled;
/**
* Bucket constructor.
*
* @param $id
* @param $name
* @param $type
*/
public function __construct($id, $name, $type, $objectLockEnabled) {
$this->id = $id;
$this->name = strtolower($name);
$this->type = $type;
$this->objectLockEnabled = $objectLockEnabled;
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function getType() {
return $this->type;
}
public function isObjectLockEnabled() {
return $this->objectLockEnabled;
}
}
final class UpdraftPlus_Backblaze_File {
protected $id;
protected $name;
protected $hash;
protected $size;
protected $type;
protected $info;
protected $bucketId;
protected $action;
protected $uploadTimestamp;
/**
* File constructor.
*
* @param $id
* @param $name
* @param $hash
* @param $size
* @param $type
* @param $info
* @param $bucketId
* @param $action
* @param $uploadTimestamp
*/
public function __construct($id, $name, $hash = null, $size = null, $type = null, $info = null, $bucketId = null, $action = null, $uploadTimestamp = null) {
$this->id = $id;
$this->name = $name;
$this->hash = $hash;
$this->size = $size;
$this->type = $type;
$this->info = $info;
$this->bucketId = $bucketId;
$this->action = $action;
$this->uploadTimestamp = $uploadTimestamp;
}
/**
* @return string
*/
public function getId() {
return $this->id;
}
/**
* @return string
*/
public function getName() {
return $this->name;
}
/**
* @return string
*/
public function getHash() {
return $this->hash;
}
/**
* @return int
*/
public function getSize() {
return $this->size;
}
/**
* @return string
*/
public function getType() {
return $this->type;
}
/**
* @return array
*/
public function getInfo()
{
return $this->info;
}
/**
* @return string
*/
public function getBucketId() {
return $this->bucketId;
}
/**
* @return string
*/
public function getAction() {
return $this->action;
}
/**
* @return string
*/
public function getUploadTimestamp() {
return $this->uploadTimestamp;
}
}
class UpdraftPlus_Backblaze_NotFoundException extends Exception {
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists