Файловый менеджер - Редактировать - /home/digilove/public_html/administrator/components/com_akeeba/BackupEngine/Postproc/Connector/Sugarsync.php
Назад
<?php /** * Akeeba Engine * * @package akeebaengine * @copyright Copyright (c)2006-2023 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\Sugarsync\Exception\Base as SugarsyncException; use Akeeba\Engine\Postproc\ProxyAware; use Akeeba\Engine\Util\FileCloseAware; use Akeeba\Engine\Util\Utf8; use DOMDocument; use DOMElement; /** * SugarSync PHP API class for Akeeba Engine */ class Sugarsync { use FileCloseAware; use ProxyAware; /** @var string The URL to the SugarSync API endpoint */ private $apiURL = 'https://api.sugarsync.com'; private $userAgent = 'AkeebaEngine/7.0.0.dev'; /** @var string The developer's access key */ private $accessKey = ''; /** @var string The developer's private key */ private $privateKey = ''; /** @var string The user's email address */ private $userEmail = ''; /** @var string The user's password */ private $userPassword = ''; /** @var string The API access token */ private $accessToken = null; /** @var string The ID of the authenticated SugarSync user */ private $userID = null; /** * Public constructor. Remember to pass a configuration array with the keys * access, private, email and password. Read the code for more info. * * @param array $config The configuration array * * @throws SugarsyncException */ public function __construct($config = []) { // Fetch the configuration parameters $this->accessKey = array_key_exists('access', $config) ? $config['access'] : ''; $this->privateKey = array_key_exists('private', $config) ? $config['private'] : ''; $this->userEmail = array_key_exists('email', $config) ? $config['email'] : ''; $this->userPassword = array_key_exists('password', $config) ? $config['password'] : ''; // Update the user agent with the version of the engine $this->userAgent = 'AkeebaEngine/' . (defined('AKEEBABACKUP_VERSION') ? AKEEBABACKUP_VERSION : AKEEBA_VERSION); } /** * Is this object connected (authenticated) to SugarSync yet? * * @return bool */ public function isConnected() { return !empty($this->accessToken) && !empty($this->userID); } /** * (Re-)Connect to SugarSync * * @param array $config Optional override for configuration parameters * * @throws SugarsyncException */ public function connect($config = []) { // Apply configuration overrides if (array_key_exists('access', $config)) { $this->accessKey = $config['access']; } if (array_key_exists('private', $config)) { $this->privateKey = $config['private']; } if (array_key_exists('email', $config)) { $this->userEmail = $config['email']; } if (array_key_exists('password', $config)) { $this->userPassword = $config['password']; } // Check that all configuration parameters are in place if (empty($this->accessKey)) { throw new SugarsyncException('You must set the developer access key'); } if (empty($this->privateKey)) { throw new SugarsyncException('You must set the developer private key'); } if (empty($this->userEmail)) { throw new SugarsyncException('You must set the user\'s email address'); } if (empty($this->userPassword)) { throw new SugarsyncException('You must set the user\'s password'); } $xml = '<?xml version="1.0" encoding="UTF-8" ?>' . "\n"; $xml .= '<authRequest>' . "\n"; $xml .= '<username>' . Utf8::utf8_encode($this->userEmail) . '</username>' . "\n"; $xml .= '<password>' . Utf8::utf8_encode($this->userPassword) . '</password>' . "\n"; $xml .= '<accessKeyId>' . Utf8::utf8_encode($this->accessKey) . '</accessKeyId>' . "\n"; $xml .= '<privateAccessKey>' . Utf8::utf8_encode($this->privateKey) . '</privateAccessKey>' . "\n"; $xml .= '</authRequest>'; $descriptor = [ 'method' => 'authorization', 'verb' => 'POST', 'data' => $xml, 'auth' => false, 'return_headers' => true, ]; $this->accessToken = null; $this->userID = null; $ret = $this->apiCall($descriptor); $result = $ret['result']; // Extract the token if (preg_match('/Location:(.*?)\r/i', $result, $m)) { $this->accessToken = $m[1]; } // Extract the user ID $userStart = strpos($result, '<user>') + 6; $userEnd = strpos($result, '</user>'); $userURL = substr($result, $userStart, $userEnd - $userStart); $userParts = explode('/', $userURL); $this->userID = array_pop($userParts); } /** * Get a list of the top-level sync folders of the user's account * * @staticvar array|null $folders Caches the sync folders list * @return array Sync folders as a display_name => internal_ID hash array */ public function getSyncFolders() { static $folders = null; if (is_null($folders)) { if (!$this->isConnected()) { $this->connect(); } $descriptor = [ 'method' => 'user/' . $this->userID . '/folders/contents', 'verb' => 'GET', ]; $ret = $this->apiCall($descriptor); $xml = $ret['result']; $dom = new DOMDocument('1.0', 'UTF-8'); $dom->loadXML($xml); $collections = $dom->getElementsByTagName('collection'); $folders = []; for ($i = 0; $i < $collections->length; $i++) { /** @var DOMElement $item */ $item = $collections->item($i); $name = $item->getElementsByTagName('displayName')->item(0)->nodeValue; $ref = $item->getElementsByTagName('ref')->item(0)->nodeValue; $refParts = explode('/', $ref); $id = array_pop($refParts); $folders[$name] = $id; } unset($dom); } return $folders; } /** * Creates a new folder and returns its ID * * @param string $container Container folder's ID or path * @param string $newFoldername The display name of the new folder * * @return string The ID of the created folder * * @throws SugarsyncException */ public function createFolder($container, $newFoldername) { if (substr($container, 0, 4) != ':sc:') { $container = $this->resolveFolder($container); } $xml = '<?xml version="1.0" encoding="UTF-8" ?>'; $xml .= '<folder><displayName>' . Utf8::utf8_encode($newFoldername) . '</displayName></folder>'; $descriptor = [ 'method' => 'folder/' . $container, 'verb' => 'POST', 'data' => $xml, 'return_headers' => true, ]; $ret = $this->apiCall($descriptor); $result = $ret['result']; // Extract the URL if (preg_match('/Location:(.*?)\r/i', $result, $m)) { $url = $m[1]; } else { $url = '/'; } $urlParts = explode('/', $url); return array_pop($urlParts); } /** * Lists all subfolders of a folder * * @param string $container Folder ID or path to list * * @return array Hashed array, folder name => folder ID * * @throws SugarsyncException */ public function getFolders($container) { if (substr($container, 0, 4) != ':sc:') { $container = $this->resolveFolder($container); } $descriptor = [ 'method' => 'folder/' . $container . '/contents?type=folder', 'verb' => 'GET', ]; $ret = $this->apiCall($descriptor); $xml = $ret['result']; $dom = new DOMDocument('1.0', 'UTF-8'); $dom->loadXML($xml); $collections = $dom->getElementsByTagName('collection'); $folders = []; for ($i = 0; $i < $collections->length; $i++) { /** @var DOMElement $item */ $item = $collections->item($i); $name = $item->getElementsByTagName('displayName')->item(0)->nodeValue; $ref = $item->getElementsByTagName('ref')->item(0)->nodeValue; $refParts = explode('/', $ref); $xid = array_pop($refParts); $folders[$name] = $xid; } unset($dom); return $folders; } /** * Lists all files of a folder * * @param string $container Folder ID or path to list * * @return array Hashed array, file name => file ID * * @throws SugarsyncException */ public function getFiles($container) { if (substr($container, 0, 4) != ':sc:') { $container = $this->resolveFolder($container); } $descriptor = [ 'method' => 'folder/' . $container . '/contents?type=file', 'verb' => 'GET', ]; $ret = $this->apiCall($descriptor); $xml = $ret['result']; $dom = new DOMDocument('1.0', 'UTF-8'); $dom->loadXML($xml); $collections = $dom->getElementsByTagName('file'); $files = []; for ($i = 0; $i < $collections->length; $i++) { /** @var DOMElement $item */ $item = $collections->item($i); $name = $item->getElementsByTagName('displayName')->item(0)->nodeValue; $ref = $item->getElementsByTagName('ref')->item(0)->nodeValue; $refParts = explode('/', $ref); $xid = array_pop($refParts); $files[$name] = $xid; } unset($dom); return $files; } /** * Uploads a file, overwriting a file by the same name if one exists. * * @param string $container Folder ID, or path to the folder, or full path to the file * @param string|null $fileName Name of the remote file, or null if a full path is provided in $container * @param string $localFile Full path to the local file to upload * * @return boolean True on success * * @throws SugarsyncException */ public function uploadFile($container, $fileName = null, $localFile = null) { if (substr($container, 0, 4) != ':sc:') { if (empty($fileName)) { $pathParts = explode('/', $container); $fileName = array_pop($pathParts); $container = implode('/', $pathParts); } $container = $this->resolveFolder($container, true); } // First check if the file already exists $files = $this->getFiles($container); if (!array_key_exists($fileName, $files)) { $fileID = $this->createFile($container, $fileName); } else { $fileID = $files[$fileName]; } $descriptor = [ 'method' => 'file/' . $fileID . '/data', 'verb' => 'PUT', 'data' => $localFile, ]; $ret = $this->apiCall($descriptor); return true; } /** * Creates an (empty) file * * @param string $container Folder ID, or path to the folder, or full path to the file * @param string|null $fileName Name of the remote file, or null if a full path is provided in $container * * @return string The file ID * * @throws SugarsyncException */ public function createFile($container, $fileName = null, $mimeType = 'application/octet-stream') { if (substr($container, 0, 4) != ':sc:') { if (empty($fileName)) { $pathParts = explode('/', $container); $fileName = array_pop($pathParts); $container = implode('/', $pathParts); } $container = $this->resolveFolder($container, true); } // First check if the file already exists $files = $this->getFiles($container); if (array_key_exists($fileName, $files)) { return $files[$fileName]; } $xml = '<?xml version="1.0" encoding="UTF-8" ?>'; $xml .= '<file>'; $xml .= '<displayName>' . Utf8::utf8_encode($fileName) . '</displayName>'; $xml .= '<mediaType>' . $mimeType . '</mediaType>'; $xml .= '</file>'; $descriptor = [ 'method' => 'folder/' . $container, 'verb' => 'POST', 'data' => $xml, 'return_headers' => true, ]; $ret = $this->apiCall($descriptor); $result = $ret['result']; // Extract the URL if (preg_match('/Location:(.*?)\r/i', $result, $m)) { $url = $m[1]; } else { $url = '/'; } $urlParts = explode('/', $url); return array_pop($urlParts); } /** * Downloads a file * * @param string $container A folder ID, or a folder path or a full path to the file to download * @param string|null $file Remote filename or null if $container is a full path * @param string|null $localFile Full path to the local file to write the data. If null, the raw file data will * be returned by this method. */ public function downloadFile($container, $file, $localFile = null) { if (substr($container, 0, 4) != ':sc:') { if (empty($file)) { $pathParts = explode('/', $container); $file = array_pop($pathParts); $container = implode('/', $pathParts); } $container = $this->resolveFolder($container); } // First check if the file already exists $files = $this->getFiles($container); if (array_key_exists($file, $files)) { $fileID = $files[$file]; } else { throw new SugarsyncException("File not found"); } $descriptor = [ 'method' => 'file/' . $fileID . '/data', 'verb' => 'GET', 'data' => $localFile, ]; $ret = $this->apiCall($descriptor); if (empty($localFile)) { return $ret['result']; } } public function deleteFile($container, $file = null) { if (substr($container, 0, 4) != ':sc:') { if (empty($file)) { $pathParts = explode('/', $container); $file = array_pop($pathParts); $container = implode('/', $pathParts); } $container = $this->resolveFolder($container); } // First check if the file already exists $files = $this->getFiles($container); if (array_key_exists($file, $files)) { $fileID = $files[$file]; } else { throw new SugarsyncException("File not found"); } $descriptor = [ 'method' => 'file/' . $fileID, 'verb' => 'DELETE', ]; $ret = $this->apiCall($descriptor); return true; } /** * Resolves a folder path to a folder ID * * @staticvar array $mappedFolders Cache of folder names to folder IDs * * @param string $folder The path to the folder * @param bool $createMissing Should I create any folders which do not exist along the way? * * @return string The folder ID */ protected function resolveFolder($folder, $createMissing = false) { static $mappedFolders = []; if (!array_key_exists($folder, $mappedFolders)) { // Break the folder into bits and pieces $folderParts = explode('/', $folder); // First, let's fetch a list of top-level sync folders $syncFolders = $this->getSyncFolders(); // Is our top-level folder really a top-level folder? if (!array_key_exists($folderParts[0], $syncFolders)) { // Treason! The user did not use a top-level folder! if (array_key_exists('Magic Briefcase', $syncFolders)) { // OK, let's use the user's "Magic Briefcase" array_unshift($folderParts, 'Magic Briefcase'); } else { // This should normally never, ever be executed $randomFolder = array_shift($syncFolders); array_unshift($syncFolders, $randomFolder); array_unshift($folderParts, 'Magic Briefcase'); } } // Get the ID of the top-level folder $toplevelFolder = array_shift($folderParts); $toplevelID = $syncFolders[$toplevelFolder]; $folderID = $this->folderReduce($folderParts, $toplevelID, $createMissing); $mappedFolders[$folder] = $folderID; } return $mappedFolders[$folder]; } /** * Recursive internal function to reduce a stack of path parts to an ID. * Used by resolveFolder(). * * @param array $stack Stack of path parts to resolve * @param string $id Folder ID relative to which I should resolve the stack * @param bool $createMissing Should I create missing folders along the way * * @return string The folder ID to which the stack resolves * * @throws SugarsyncException */ protected function folderReduce($stack, $id, $createMissing = false) { // Is the path fully reduced? if (empty($stack)) { return $id; } // No? Get the next path fragment $search = array_shift($stack); // If the fragment is empty the path is, in fact, fully reduced. if (empty($search)) { return $id; } $descriptor = [ 'method' => 'folder/' . $id . '/contents?type=folder', 'verb' => 'GET', ]; $ret = $this->apiCall($descriptor); $xml = $ret['result']; $dom = new DOMDocument('1.0', 'UTF-8'); $dom->loadXML($xml); $collections = $dom->getElementsByTagName('collection'); $folders = []; for ($i = 0; $i < $collections->length; $i++) { /** @var DOMElement $item */ $item = $collections->item($i); $name = $item->getElementsByTagName('displayName')->item(0)->nodeValue; $ref = $item->getElementsByTagName('ref')->item(0)->nodeValue; $refParts = explode('/', $ref); $xid = array_pop($refParts); $folders[$name] = $xid; } unset($dom); if (array_key_exists($search, $folders)) { // Folder found; recurse return $this->folderReduce($stack, $folders[$search], $createMissing); } else { // The folder was not found if ($createMissing) { $newId = $this->createFolder($id, $search); return $this->folderReduce($stack, $newId, $createMissing); } else { throw new SugarsyncException("The requested folder could not be located in your SugarSync account"); } } } /** * Calls SugarSync's API and returns the results * * @param array $descriptor An array describing the API call you want to make * * @return array * @throws SugarsyncException */ protected function apiCall($descriptor = []) { // Get data from descriptor $suffix = array_key_exists('method', $descriptor) ? $descriptor['method'] : ''; $data = array_key_exists('data', $descriptor) ? $descriptor['data'] : ''; $verb = array_key_exists('verb', $descriptor) ? $descriptor['verb'] : 'GET'; $auth = array_key_exists('auth', $descriptor) ? $descriptor['auth'] : true; $heads = array_key_exists('headers', $descriptor) ? $descriptor['headers'] : []; $returnHeaders = array_key_exists('return_headers', $descriptor) ? $descriptor['return_headers'] : false; $silenceErrors = array_key_exists('shutup', $descriptor) ? $descriptor['shutup'] : false; // Make sure the HTTP verb is a supported one if (!in_array($verb, ['GET', 'POST', 'PUT', 'DELETE'])) { $verb = 'GET'; } // Calculate the URL $url = $this->apiURL . '/' . $suffix; // Create the HTTP headers array $headers = [ 'Expect:', ]; $headers = array_merge($headers, $heads); // Handle extra headers for authorised API calls if ($auth && !$this->isConnected()) { $this->connect(); } if ($auth) { $headers[] = 'Authorization: ' . $this->accessToken; } $ch = curl_init($url); $this->applyProxySettingsToCurl($ch); curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent); @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); @curl_setopt($ch, CURLOPT_CAINFO, AKEEBA_CACERT_PEM); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); $fp = null; switch ($verb) { case 'POST': curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_POST, true); $headers[] = 'Content-Type: application/xml; charset=UTF-8'; $headers[] = 'Content-Length: ' . strlen($data); break; case 'PUT': if (is_file($data) && is_readable($data)) { $headers[] = 'Content-Length: ' . filesize($data); $fp = fopen($data, 'r'); curl_setopt($ch, CURLOPT_PUT, true); curl_setopt($ch, CURLOPT_INFILE, $fp); curl_setopt($ch, CURLOPT_INFILESIZE, filesize($data)); // Some broken cURL versions cause an error. Forcing HTTP/1.1 seems to be fixing it. if (defined('CURLOPT_HTTP_VERSION') && defined('CURL_HTTP_VERSION_1_1')) { curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); } } else { throw new SugarsyncException("$data is not readable; can not upload to SugarSync"); } break; case 'DELETE': curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE'); break; case 'GET': if (!empty($data)) { $fp = fopen($data, 'w'); curl_setopt($ch, CURLOPT_FILE, $fp); } curl_setopt($ch, CURLOPT_POST, false); break; } curl_setopt($ch, CURLINFO_HEADER_OUT, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); if ($returnHeaders) { curl_setopt($ch, CURLOPT_HEADER, true); } $result = curl_exec($ch); $info = curl_getinfo($ch); $errno = curl_errno($ch); $error = curl_error($ch); @curl_close($ch); if (!is_null($fp)) { $this->conditionalFileClose($fp); } if (!$silenceErrors && ($errno != 0)) { throw new SugarsyncException("Network error [$errno]: $error"); } $ret = [ 'result' => $result, 'info' => $info, 'errno' => $errno, 'error' => $error, ]; $http_code = $info['http_code']; if ($silenceErrors || (($http_code >= 200) && ($http_code <= 299))) { return $ret; } if ($http_code == 400) { throw new SugarsyncException("HTTP Error [$http_code]: Required information was not provided to SugarSync"); } if ($http_code == 401) { throw new SugarsyncException("HTTP Error [$http_code]: The credentials were rejected by SugarSync. Check the Access Key ID, Private Access Key, Email and Password in your configuration."); } if ($http_code == 403) { throw new SugarsyncException("HTTP Error [$http_code]: Failed authentication."); } if ($http_code == 404) { throw new SugarsyncException("HTTP Error [$http_code]: Not found."); } throw new SugarsyncException("HTTP Error [$http_code]: Server Error; SugarSync's API service may be down or experiencing a technical problem"); } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Генерация страницы: 0.39 |
proxy
|
phpinfo
|
Настройка