BxDolStorageS3.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup UnaCore UNA Core
  7. * @{
  8. */
  9. if (!defined('CURL_SSLVERSION_TLSv1'))
  10. define('CURL_SSLVERSION_TLSv1', 1);
  11. /**
  12. * File storage in Amazon S3.
  13. * @see BxDolStorage
  14. */
  15. class BxDolStorageS3 extends BxDolStorage
  16. {
  17. protected $_s3;
  18. protected $_sBucket;
  19. protected $_sDomain;
  20. protected $_sEndpoint;
  21. protected $_bSSL;
  22. protected $_bReducedRedundancy;
  23. /**
  24. * constructor
  25. */
  26. public function __construct($aObject)
  27. {
  28. parent::__construct($aObject);
  29. $this->_bSSL = (isset($this->_aParams['ssl']) && $this->_aParams['ssl']) || (!isset($this->_aParams['ssl']) && strncmp(BX_DOL_URL_ROOT, 'https://', 8) === 0) ? true : false;
  30. $this->_sEndpoint = getParam('sys_storage_s3_endpoint');
  31. $this->_sBucket = getParam('sys_storage_s3_bucket');
  32. $this->_sDomain = getParam('sys_storage_s3_domain');
  33. $this->_bReducedRedundancy = isset($this->_aParams['reduced_redundancy']) && $this->_aParams['reduced_redundancy'] ? true : false;
  34. $this->init($aObject);
  35. }
  36. protected function init ($aObject)
  37. {
  38. require_once(BX_DIRECTORY_PATH_PLUGINS . 'unacms/amazon-s3-php-class-hmac-v2/S3.php');
  39. $this->_s3 = new S3(
  40. getParam('sys_storage_s3_access_key'),
  41. getParam('sys_storage_s3_secret_key'),
  42. $this->_bSSL,
  43. $this->_sEndpoint ? $this->_sEndpoint : 's3.amazonaws.com'
  44. );
  45. if ($this->_bSSL && getParam('sys_curl_ssl_allow_untrusted'))
  46. $this->_s3->setSSL($this->_bSSL, false);
  47. $this->_s3->setExceptions(true);
  48. }
  49. /**
  50. * Get file url.
  51. * @param $iFileId file
  52. * @return file url
  53. */
  54. public function getFileUrlById($iFileId)
  55. {
  56. $aFile = $this->_oDb->getFileById($iFileId);
  57. if (!$aFile)
  58. return false;
  59. if ($this->isAuthUrl($aFile)) {
  60. $sFileLocation = $this->getObjectBaseDir($aFile['private']) . $aFile['path'];
  61. if ($this->_sDomain)
  62. return $this->_s3->getAuthenticatedURL($this->_sDomain, $sFileLocation, $this->_aObject['token_life'], true, $this->_bSSL);
  63. else
  64. return $this->_s3->getAuthenticatedURL($this->_sBucket, $sFileLocation, $this->_aObject['token_life'], false, $this->_bSSL);
  65. }
  66. return $this->getObjectBaseUrl($aFile['private']) . $aFile['path'];
  67. }
  68. /**
  69. * Start file downloading by remote id. If file is private then token is checked.
  70. */
  71. public function download ($sRemoteId, $sToken = false, $bForceDownloadDialog = 'auto')
  72. {
  73. $this->setErrorCode(BX_DOL_STORAGE_ERR_OK);
  74. $aFile = $this->_oDb->getFileByRemoteId($sRemoteId);
  75. if (!$aFile) {
  76. $this->setErrorCode(BX_DOL_STORAGE_ERR_FILE_NOT_FOUND);
  77. return false;
  78. }
  79. if ($aFile['private'] && !$this->_oDb->isTokenValid($aFile['id'], $sToken)) {
  80. $this->setErrorCode(BX_DOL_STORAGE_ERR_PERMISSION_DENIED);
  81. return false;
  82. }
  83. $sUrl = $this->getFileUrlById($aFile['id']);
  84. if ($bForceDownloadDialog && $bForceDownloadDialog !== 'auto') {
  85. // download remote file to tmp
  86. $sTmpFilePath = BX_DIRECTORY_PATH_TMP . 'dwnld_'.$sRemoteId;
  87. if (!file_exists($sTmpFilePath)) {
  88. @file_put_contents($sTmpFilePath, bx_file_get_contents($sUrl));
  89. }
  90. // read from a local storage to be able to send it as attachment and give it a proper name
  91. if (!file_exists($sTmpFilePath) || !bx_smart_readfile($sTmpFilePath, $aFile['file_name'], $aFile['mime_type'], $aFile['private'] && $this->_iCacheControl > $this->_aObject['token_life'] ? $this->_aObject['token_life'] : $this->_iCacheControl, $aFile['private'] ? 'private' : 'public', 'attachment')) {
  92. $this->setErrorCode(BX_DOL_STORAGE_ERR_ENGINE_GET);
  93. return false;
  94. }
  95. } else {
  96. header("Location: " . $sUrl);
  97. }
  98. return parent::download($aFile);
  99. }
  100. /**
  101. * Set file private or public.
  102. */
  103. public function setFilePrivate($iFileId, $isPrivate = true)
  104. {
  105. $aFile = $this->_oDb->getFileById($iFileId);
  106. if (!$aFile)
  107. return false;
  108. try {
  109. $sFileLocation = $this->getObjectBaseDir($aFile['private']) . $aFile['path'];
  110. if (($aACP = $this->_s3->getAccessControlPolicy($this->_sBucket, $sFileLocation)) === false) {
  111. $this->setErrorCode(BX_DOL_STORAGE_ERR_ENGINE_GET);
  112. return false;
  113. }
  114. if (!is_array($aACP['acl']) || !$aACP['acl']) {
  115. $this->setErrorCode(BX_DOL_STORAGE_ERR_ENGINE_GET);
  116. return false;
  117. }
  118. // check current permissions
  119. $aNewACP = $aACP;
  120. unset($aNewACP['acl']);
  121. $aNewACP['acl'] = array();
  122. $aGroupPublic = false;
  123. $aGroupPrivate = false;
  124. foreach ($aACP['acl'] as $r) {
  125. if ('Group' == $r['type']) {
  126. if (isset($r['uri']) && $r['uri'] == 'http://acs.amazonaws.com/groups/global/AllUsers')
  127. $aGroupPublic = $r;
  128. elseif (isset($r['uri']) && $r['uri'] == 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers')
  129. $aGroupPrivate = $r;
  130. else
  131. $aNewACP['acl'][] = $r;
  132. } else {
  133. $aNewACP['acl'][] = $r;
  134. }
  135. }
  136. // determine permissions changing
  137. $aGroupAdd = false;
  138. if ($isPrivate && (!$aGroupPrivate || $aGroupPublic)) {
  139. // make private
  140. $aGroupAdd = array (
  141. 'type' => 'Group',
  142. 'uri' => 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers',
  143. 'permission' => 'READ',
  144. );
  145. } elseif (!$isPrivate && ($aGroupPrivate || !$aGroupPublic)) {
  146. // make public
  147. $aGroupAdd = array (
  148. 'type' => 'Group',
  149. 'uri' => 'http://acs.amazonaws.com/groups/global/AllUsers',
  150. 'permission' => 'READ',
  151. );
  152. }
  153. // change permission if necessary
  154. if ($aGroupAdd) {
  155. $aNewACP['acl'][] = $aGroupAdd;
  156. if (!$this->_s3->setAccessControlPolicy($this->_sBucket, $sFileLocation, $aNewACP)) {
  157. $this->setErrorCode(BX_DOL_STORAGE_ERR_ENGINE_GET);
  158. return false;
  159. }
  160. }
  161. } catch (Exception $e) {
  162. $this->setErrorCode(BX_DOL_STORAGE_ERR_ENGINE_GET);
  163. bx_log('sys_storage_s3', $e->getMessage(), BX_LOG_ERR);
  164. return false;
  165. }
  166. return parent::setFilePrivate($iFileId, $isPrivate);
  167. }
  168. // ----------------
  169. protected function generateHeaders($sFileName, $isPrivate, $sMimeType = '')
  170. {
  171. if (!$sMimeType)
  172. $sMimeType = $this->getMimeTypeByFileName($sFileName);
  173. $aRequestHeaders = array (
  174. "Content-Type" => $sMimeType,
  175. );
  176. if ($this->_iCacheControl > 0) {
  177. $aRequestHeaders = array_merge ($aRequestHeaders, array (
  178. "Cache-Control" => "max-age=" . ($isPrivate && $this->_iCacheControl > $this->_aObject['token_life'] ? $this->_aObject['token_life'] : $this->_iCacheControl),
  179. ));
  180. }
  181. return $aRequestHeaders;
  182. }
  183. protected function addFileToEngine($sTmpFile, $sLocalId, $sName, $isPrivate, $iProfileId)
  184. {
  185. try {
  186. $sMimeType = $this->getMimeTypeByFileName($sName);
  187. $sExt = $this->getFileExt($sName);
  188. $sPath = $this->genPath($sLocalId, $this->_aObject['levels']);
  189. $sRemoteNamePath = $sPath . $sLocalId . ($sExt ? '.' . $sExt : '');
  190. $aRequestHeaders = $this->generateHeaders($sName, $isPrivate);
  191. $aMetaHeaders = array();
  192. $sStorageClass = $this->_bReducedRedundancy ? S3::STORAGE_CLASS_RRS : S3::STORAGE_CLASS_STANDARD;
  193. $sACL = $isPrivate ? S3::ACL_AUTHENTICATED_READ : S3::ACL_PUBLIC_READ;
  194. $aInputFile = $this->_s3->inputFile($sTmpFile);
  195. if (!$this->_s3->putObject($aInputFile, $this->_sBucket, $this->getObjectBaseDir($isPrivate) . $sRemoteNamePath, $sACL, $aMetaHeaders, $aRequestHeaders, $sStorageClass)) {
  196. $this->setErrorCode(BX_DOL_STORAGE_ERR_ENGINE_ADD);
  197. return false;
  198. }
  199. } catch (Exception $e) {
  200. $this->setErrorCode(BX_DOL_STORAGE_ERR_ENGINE_ADD);
  201. bx_log('sys_storage_s3', $e->getMessage(), BX_LOG_ERR);
  202. return false;
  203. }
  204. return true;
  205. }
  206. protected function deleteFileFromEngine($sFilePath, $isPrivate)
  207. {
  208. $sFileLocation = $this->getObjectBaseDir($isPrivate) . $sFilePath;
  209. try {
  210. if (!$this->_s3->deleteObject($this->_sBucket, $sFileLocation)) {
  211. $this->setErrorCode(BX_DOL_STORAGE_ERR_UNLINK);
  212. return false;
  213. }
  214. } catch (Exception $e) {
  215. $this->setErrorCode(BX_DOL_STORAGE_ERR_UNLINK);
  216. bx_log('sys_storage_s3', $e->getMessage(), BX_LOG_ERR);
  217. return false;
  218. }
  219. return true;
  220. }
  221. protected function genRemoteNamePath ($sPath, $sLocalId, $sExt)
  222. {
  223. return $sPath . $sLocalId . ($sExt ? '.' . $sExt : '');
  224. }
  225. protected function getObjectBaseDir ($isPrivate = false)
  226. {
  227. return $this->_aObject['object'] . '/';
  228. }
  229. protected function getObjectBaseUrl ($isPrivate = false)
  230. {
  231. $sPath = $this->_bSSL ? 'https://' : 'http://';
  232. if ($this->_sDomain)
  233. $sPath .= $this->_sDomain;
  234. elseif ($this->_sEndpoint)
  235. $sPath .= $this->_sEndpoint . '/' . $this->_sBucket;
  236. else
  237. $sPath .= $this->_sBucket . '.s3.amazonaws.com';
  238. return $sPath . '/' . $this->getObjectBaseDir($isPrivate);
  239. }
  240. }
  241. /** @} */