File Manager

Current Directory: /home/astoriaah/www/administrator/components/com_akeeba/BackupEngine/Postproc/Connector
Viewing File: /home/astoriaah/www/administrator/components/com_akeeba/BackupEngine/Postproc/Connector/Swift.php
<?php /** * Akeeba Engine * * @package akeebaengine * @copyright Copyright (c)2006-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace Akeeba\Engine\Postproc\Connector; defined('AKEEBAENGINE') || die(); use Akeeba\Engine\Postproc\Connector\Cloudfiles\Exception\Http; use Akeeba\Engine\Postproc\Connector\Cloudfiles\Request; use Akeeba\Engine\Util\FileCloseAware; use DateTime; use stdClass; /** * Generic OpenStack SWIFT object storage API implementation */ class Swift { use FileCloseAware; /** * The authentication server (Keystone) endpoint, e.g. https://auth.cloud.ovh.net/v2.0 * * @var string * @since 6.1.0 */ protected $authEndpoint = ''; /** * The endpoint for accessing the storage container, e.g. https://storage.de1.cloud.ovh.net/v1/AUTH_abcdef0123456789abcdef0123456789/my-container * * @var string * @since 6.1.0 */ protected $storageEndpoint = ''; /** * The token retrieved from the authentication service (Keystone) * * @var string * @since 6.1.0 */ protected $token; /** * The expiration timestamp of the token * * @var int * @since 6.1.0 */ protected $tokenExpiration; /** * The tenant ID of the OpenStack cloud * * @var string * @since 6.1.0 */ protected $tenantId; /** * The OpenStack username * * @var string * @since 6.1.0 */ protected $username; /** * The OpenStack password * * @var string * @since 6.1.0 */ protected $password; /** * The endpoints of the SWIFT service, as returned by the Keystone service, indexed by region. Each endpoint is * raw object. * * @var stdClass[] * @since 6.1.0 */ protected $endPoints = []; /** * A callable which is passed the authentication information and result to cater for non-standard SWIFT * implementations. * * @var callable * @since 6.1.0 */ protected $authenticationCallback; /** * Swift constructor. * * @param string $authEndpoint The authentication endpoint URL. * @param string $tenantId The OpenStack tenant ID. * @param string $username The OpenStack username. * @param string $password The OpenStack password. * * * @since 6.1.0 */ public function __construct($authEndpoint, $tenantId, $username, $password) { $this->authEndpoint = $authEndpoint; $this->tenantId = $tenantId; $this->username = $username; $this->password = $password; } /** * Get the authentication endpoint URL * * @return string * @since 6.1.0 */ public function getAuthEndpoint() { return $this->authEndpoint; } /** * Set the authentication endpoint * * @param string $authEndpoint The new authentication endpoint URL * * @return Swift * @since 6.1.0 */ public function setAuthEndpoint($authEndpoint) { $this->authEndpoint = $authEndpoint; return $this; } /** * Get the storage container's endpoint URL * * @return string * @since 6.1.0 */ public function getStorageEndpoint() { return $this->storageEndpoint; } /** * Set the storage container's endpoint URL * * @param string $storageEndpoint The storage container's endpoint URL * * @return Swift * @since 6.1.0 */ public function setStorageEndpoint($storageEndpoint) { $this->storageEndpoint = $storageEndpoint; return $this; } /** * @return string */ public function getTenantId() { return $this->tenantId; } /** * @param string $tenantId * * @return Swift */ public function setTenantId($tenantId) { $this->tenantId = $tenantId; return $this; } /** * @return string */ public function getUsername() { return $this->username; } /** * @param string $username * * @return Swift */ public function setUsername($username) { $this->username = $username; return $this; } /** * @return string */ public function getPassword() { return $this->password; } /** * @param string $password * * @return Swift */ public function setPassword($password) { $this->password = $password; return $this; } /** * @return int */ public function getTokenExpiration() { return $this->tokenExpiration; } /** * @return stdClass[] */ public function getEndPoints() { return $this->endPoints; } /** * Get the token. If the token is expired or missing, or if the force parameter is set, we will re-authenticate * against the OpenStack cloud. * * @param bool $force Tru to force re-authentication * * @return string * @throws Http * @since 6.1.0 * */ public function getToken($force = false) { // Am I forced to get a fresh token? if ($force) { $this->authenticate(); } // If I don't have a token I must authenticate if (empty($this->token)) { $this->authenticate(); } // The expiration time of the token is less than an hour away. Let's re-authenticate. if ($this->tokenExpiration < (time() + 3600)) { $this->authenticate(); } return $this->token; } /** * Lists the containers in the SWIFT account * * @param bool $assoc Should I return an associative array, where the key is the container name? (def: no) * @param string $lastContainer Start listing AFTER this last container (pagination) * @param int $limit How many containers to list * * @return stdClass[] Array or objects. Internal objects have keys count, bytes, name * * @throws Http * @since 6.1.0 */ public function listContainers($assoc = false, $lastContainer = null, $limit = 10000) { // Re-authenticate if necessary $token = $this->getToken(); // Get the URL to list containers. It's the container endpoint minus the actual container name. $url = $this->getStorageEndpoint(); $lastSlash = strrpos($url, '/'); $url = substr($url, 0, $lastSlash); // Get the request object $request = new Request('GET', $url); $request->setHeader('X-Auth-Token', $token); $request->setHeader('Accept', 'application/json'); $request->setParameter('format', 'json'); if (!empty($lastContainer)) { $request->setParameter('marker', $lastContainer); } if (!is_numeric($limit)) { $limit = 10000; } if ($limit <= 0) { $limit = 10000; } $request->setParameter('limit', $limit); $response = $request->getResponse(); if (!$assoc) { return $response->body; } $ret = []; if (!empty($response->body)) { foreach ($response->body as $container) { $ret[$container->name] = $container; } } return $ret; } /** * Lists the contents of a directory inside the container * * @param string $path The path to the directory you want to list, '' for the root. * @param bool $assoc Should I return an associative array with filenames as keys? * @param null $lastEntry The entry AFTER which to start listing * @param int $limit How many files to show (1000 by default) * @param string $prefix The common prefix of files to list * * @return array Array of objects. Object keys: hash, last_modified, bytes, name, content_type * @throws Http * @since 6.1.0 * */ public function listContents($path = '', $assoc = false, $lastEntry = null, $limit = 1000, $prefix = '') { // Re-authenticate if necessary $token = $this->getToken(); // Get the URL to list containers $url = $this->getStorageEndpoint(); $url = rtrim($url, '\\/'); $path = ltrim($path, '\\/'); $url .= '/' . $path; // Get the request object $request = new Request('GET', $url); $request->setHeader('X-Auth-Token', $token); $request->setHeader('Accept', 'application/json'); $request->setParameter('format', 'json'); if (!empty($lastEntry)) { $request->setParameter('marker', $lastEntry); } if (!empty($prefix)) { $request->setParameter('prefix', $prefix); } if (!is_numeric($limit)) { $limit = 1000; } if ($limit <= 0) { $limit = 1000; } $request->setParameter('limit', $limit); $request->setParameter('delimiter', '/'); $response = $request->getResponse(); if (!$assoc) { return $response->body; } $ret = []; if (!empty($response->body)) { foreach ($response->body as $file) { $ret[$file->name] = $file; } } return $ret; } /** * Uploads a file. The $input array can have one of the following formats: * * 1. A string with the contents of the file to be put to CloudFiles * * 2. An array('fp' => $fp) containing a file pointer, open in read binary mode, to the file to upload * * 3. An array('file' => $pathToFile) containing the path to the file to upload * * 4. An array('data' => $rawData) which is the same as passing a string (case 1) * * When using an array you can also pass the following optional parameters in the array: * size The size of the uploaded content in bytes * * @param string|array $input See the method description * @param string $path The path inside the container of the uploaded file * @param string $contentType The content type of the uploaded file * * @throws Http * @since 6.1.0 * */ public function putObject($input, $path, $contentType = null) { // Re-authenticate if necessary $token = $this->getToken(); // Get the URL to list containers $url = $this->getStorageEndpoint(); $url = rtrim($url, '\\/'); $path = ltrim($path, '\\/'); $url .= '/' . $path; // Get the request object $request = new Request('PUT', $url); $request->setHeader('X-Auth-Token', $token); // Decide what to do based on the $input format if (is_string($input)) { $input = [ 'data' => $input, 'size' => strlen($input), ]; } // Data if (isset($input['fp'])) { $request->fp = $input['fp']; } elseif (isset($input['file'])) { $request->fp = @fopen($input['file'], 'r'); } elseif (isset($input['data'])) { $request->data = $input['data']; } // Content-Length (required) if (isset($input['size']) && $input['size'] >= 0) { $request->size = $input['size']; } else { if (isset($input['file'])) { clearstatcache(false, $input['file']); $request->size = @filesize($input['file']); } elseif (isset($input['data'])) { $request->size = strlen($input['data']); } } if (empty($contentType)) { $contentType = 'application/octet-stream'; } $request->setParameter('Content-Type', $contentType); $request->setParameter('Content-Length', $request->size); $request->getResponse(); if (isset($input['file'])) { $this->conditionalFileClose($request->fp); } } /** * Downloads a file from CloudFiles back to your server * * @param string $path The path to the file to download * @param resource $fp A file pointer, opened in write binary mode, to write out the downloaded file * @param array $headers An array of headers to send during the download, e.g. ['Range' => '1-100'] * * @return void * * @throws Http * @since 6.1.0 * */ public function downloadObject($path, &$fp, $headers = []) { // Re-authenticate if necessary $token = $this->getToken(); // Get the URL to list containers $url = $this->getStorageEndpoint(); $url = rtrim($url, '\\/'); $path = ltrim($path, '\\/'); $url .= '/' . $path; // Get the request object $request = new Request('GET', $url); $request->setHeader('X-Auth-Token', $token); if (!empty($headers)) { foreach ($headers as $k => $v) { $request->setHeader($k, $v); } } $request->fp = $fp; $request->getResponse(); } /** * Delete a file from CloudFiles * * @param string $path The path to the file to download * * @return void * * @throws Http * @since 6.1.0 * */ public function deleteObject($path) { // Re-authenticate if necessary $token = $this->getToken(); // Get the URL to list containers $url = $this->getStorageEndpoint(); $url = rtrim($url, '\\/'); $path = ltrim($path, '\\/'); $url .= '/' . $path; // Get the request object $request = new Request('DELETE', $url); $request->setHeader('X-Auth-Token', $token); $request->getResponse(); } /** * Authenticate to the OpenStack cloud and retrieve a fresh token * * @return string * @throws Http * * @since 6.1.0 */ protected function authenticate() { // Send the token request to Keystone $message = [ 'auth' => [ 'tenantId' => $this->tenantId, 'passwordCredentials' => [ 'username' => $this->username, 'password' => $this->password, ], ], ]; $json = json_encode($message); $url = rtrim($this->authEndpoint, '/') . '/tokens'; $request = new Request('POST', $url); $request->data = $json; $request->setHeader('Accept', 'application/json'); $request->setHeader('Content-Type', 'application/json'); $request->setHeader('Content-Length', strlen($request->data)); $response = $request->getResponse(); // Get the tenant ID $this->tenantId = $response->body->access->token->tenant->id; // Get the token and its expiration $this->token = $response->body->access->token->id; $date = new DateTime($response->body->access->token->expires); $this->tokenExpiration = $date->getTimestamp(); // Loop through the serviceCatalog and index the Swift endpoints if (isset($request->body) && isset($request->body->serviceCatalog)) { foreach ($request->body->serviceCatalog as $service) { if ($service->name != 'swift') { continue; } if (!isset($service->endpoints)) { continue; } foreach ($service->endpoints as $endpoint) { $this->endPoints[$endpoint->region] = $endpoint->publicURL; } } } // Callback if (is_callable($this->authenticationCallback)) { call_user_func_array($this->authenticationCallback, [&$this, $response]); } return $this->token; } }