BxDolStudioInstallerUtils.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
  4. * CC-BY License - http://creativecommons.org/licenses/by/3.0/
  5. *
  6. * @defgroup DolphinStudio Dolphin Studio
  7. * @{
  8. */
  9. bx_import('BxDolInstallerUtils');
  10. define('BX_DOL_UNITY_URL_ROOT', 'http://www.boonex.com/');
  11. define('BX_DOL_UNITY_URL_MARKET', BX_DOL_UNITY_URL_ROOT . 'market/');
  12. class BxDolStudioInstallerUtils extends BxDolInstallerUtils implements iBxDolSingleton
  13. {
  14. public function __construct()
  15. {
  16. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  17. trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
  18. parent::__construct();
  19. }
  20. /**
  21. * Prevent cloning the instance
  22. */
  23. public function __clone()
  24. {
  25. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  26. trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
  27. }
  28. /**
  29. * Get singleton instance of the class
  30. */
  31. public static function getInstance()
  32. {
  33. if (!isset($GLOBALS['bxDolClasses'][__CLASS__]))
  34. $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolStudioInstallerUtils();
  35. return $GLOBALS['bxDolClasses'][__CLASS__];
  36. }
  37. public function serviceDownloadFile($aItem, $bUseFtp)
  38. {
  39. return $this->performDownload($aItem, $bUseFtp);
  40. }
  41. public function servicePerform($sDirectory, $sOperation, $aParams)
  42. {
  43. return $this->performAction($sDirectory, $sOperation, $aParams);
  44. }
  45. public function getModules()
  46. {
  47. $aModules = array();
  48. $oTemplate = BxDolStudioTemplate::getInstance();
  49. $aInstalledPathes = $aInstalledInfo = array();
  50. $this->getInstalledInfo($aInstalledPathes, $aInstalledInfo);
  51. $sPath = BX_DIRECTORY_PATH_MODULES;
  52. if(($rHandleVendor = opendir($sPath)) !== false) {
  53. while(($sVendor = readdir($rHandleVendor)) !== false) {
  54. if(substr($sVendor, 0, 1) == '.' || !is_dir($sPath . $sVendor))
  55. continue;
  56. if(($rHandleModule = opendir($sPath . $sVendor)) !== false) {
  57. while(($sModule = readdir($rHandleModule)) !== false) {
  58. if(!is_dir($sPath . $sVendor . '/' . $sModule) || substr($sModule, 0, 1) == '.')
  59. continue;
  60. $sConfigPath = $sPath . $sVendor . '/' . $sModule . '/install/config.php';
  61. $aModule = $this->getConfigModule($sConfigPath, $aInstalledPathes, $aInstalledInfo);
  62. if(empty($aModule))
  63. continue;
  64. $aModules[$aModule['title']] = $aModule;
  65. }
  66. closedir($rHandleModule);
  67. }
  68. }
  69. closedir($rHandleVendor);
  70. }
  71. ksort($aModules);
  72. return $aModules;
  73. }
  74. public function getUpdates()
  75. {
  76. $aUpdates = array();
  77. bx_import('BxDolStudioTemplate');
  78. $oTemplate = BxDolStudioTemplate::getInstance();
  79. $aInstalledPathes = $aInstalledInfo = array();
  80. $this->getInstalledInfo($aInstalledPathes, $aInstalledInfo);
  81. $sPath = BX_DIRECTORY_PATH_MODULES;
  82. if(($rHandleVendor = opendir($sPath)) !== false) {
  83. while(($sVendor = readdir($rHandleVendor)) !== false) {
  84. if(substr($sVendor, 0, 1) == '.' || !is_dir($sPath . $sVendor))
  85. continue;
  86. if(($rHandleModule = opendir($sPath . $sVendor . '/')) !== false) {
  87. while(($sModule = readdir($rHandleModule)) !== false) {
  88. if(!is_dir($sPath . $sVendor . '/' . $sModule) || substr($sModule, 0, 1) == '.')
  89. continue;
  90. if(($rHandleUpdate = @opendir($sPath . $sVendor . '/' . $sModule . '/updates/')) !== false) {
  91. while(($sUpdate = readdir($rHandleUpdate)) !== false) {
  92. if(!is_dir($sPath . $sVendor . '/' . $sModule . '/updates/' . $sUpdate) || substr($sUpdate, 0, 1) == '.')
  93. continue;
  94. $sConfigPathModule = $sPath . $sVendor . '/' . $sModule . '/install/config.php';
  95. $sConfigPathUpdate = $sPath . $sVendor . '/' . $sModule . '/updates/' . $sUpdate . '/install/config.php';
  96. $aUpdate = $this->getConfigUpdate($sConfigPathModule, $sConfigPathUpdate, $aInstalledPathes, $aInstalledInfo);
  97. if(empty($aUpdate))
  98. continue;
  99. $aUpdates[$aUpdate['title']] = $aUpdate;
  100. }
  101. closedir($rHandleUpdate);
  102. }
  103. }
  104. closedir($rHandleModule);
  105. }
  106. }
  107. closedir($rHandleVendor);
  108. }
  109. ksort($aUpdates);
  110. return $aUpdates;
  111. }
  112. public function perform($sDirectory, $sOperation, $aParams = array())
  113. {
  114. if(self::isRealOwner())
  115. return $this->performAction($sDirectory, $sOperation, $aParams);
  116. bx_import('BxDolCronQuery');
  117. if(BxDolCronQuery::getInstance()->addTransientJobService('sys_perform_install', array('system', 'perform', array($sDirectory, $sOperation, $aParams), 'DolStudioInstallerUtils')))
  118. return array('code' => 1, 'message' => _t('_adm_mod_msg_process_operation_scheduled'));
  119. return array(
  120. 'code' => 2,
  121. 'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory),
  122. );
  123. }
  124. public function checkUpdates($bAuthorizedAccess = false)
  125. {
  126. bx_import('BxDolModuleQuery');
  127. $aModules = BxDolModuleQuery::getInstance()->getModules();
  128. $aProducts = array();
  129. foreach($aModules as $aModule) {
  130. if(empty($aModule['name']))
  131. continue;
  132. $aProducts[] = array(
  133. 'name' => $aModule['name'],
  134. 'version' => $aModule['version'],
  135. 'hash' => $aModule['hash'],
  136. );
  137. }
  138. $sProducts = base64_encode(serialize($aProducts));
  139. if($bAuthorizedAccess) {
  140. bx_import('BxDolStudioOAuth');
  141. return BxDolStudioOAuth::getInstance()->loadItems(array('dol_type' => 'available_updates', 'dol_products' => $sProducts));
  142. }
  143. bx_import('BxDolStudioJson');
  144. return BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_updates', array(
  145. 'products' => $sProducts,
  146. 'domain' => BX_DOL_URL_ROOT,
  147. 'user' => (int)getParam('sys_oauth_user')
  148. ));
  149. }
  150. public function downloadFileAuthorized($iFileId)
  151. {
  152. bx_import('BxDolStudioOAuth');
  153. $aItem = BxDolStudioOAuth::getInstance()->loadItems(array('dol_type' => 'product_file', 'dol_file_id' => $iFileId));
  154. return $this->downloadFile($aItem);
  155. }
  156. public function downloadUpdatePublic($sModuleName)
  157. {
  158. bx_import('BxDolModuleQuery');
  159. $aModule = BxDolModuleQuery::getInstance()->getModuleByName($sModuleName);
  160. bx_import('BxDolStudioJson');
  161. $aItem = BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_download_update', array(
  162. 'product' => base64_encode(serialize(array(
  163. 'name' => $aModule['name'],
  164. 'version' => $aModule['version'],
  165. 'hash' => $aModule['hash'],
  166. ))),
  167. 'domain' => BX_DOL_URL_ROOT,
  168. 'user' => (int)getParam('sys_oauth_user')
  169. ));
  170. return $this->downloadFile($aItem);
  171. }
  172. protected function downloadFile($aItem, $bUseFtp = BX_FORCE_USE_FTP_FILE_TRANSFER)
  173. {
  174. if(self::isRealOwner())
  175. return $this->performDownload($aItem, $bUseFtp);
  176. bx_import('BxDolCronQuery');
  177. if(BxDolCronQuery::getInstance()->addTransientJobService('sys_perform_download', array('system', 'download_file', array($aItem, $bUseFtp), 'DolStudioInstallerUtils')))
  178. return _t('_adm_str_msg_download_scheduled');
  179. return false;
  180. }
  181. protected function performDownload($aItem, $bUseFtp)
  182. {
  183. if(empty($aItem) || !is_array($aItem))
  184. return $aItem;
  185. //--- write ZIP archive.
  186. $sFilePath = '';
  187. $mixedResult = $this->performWrite($aItem, $sFilePath);
  188. if($mixedResult !== true)
  189. return $mixedResult;
  190. //--- Unarchive package.
  191. $sPackagePath = '';
  192. $mixedResult = $this->performUnarchive($sFilePath, $sPackagePath);
  193. @unlink($sFilePath);
  194. if($mixedResult !== true)
  195. return $mixedResult;
  196. //--- Copy unarchived package.
  197. $mixedResult = $this->performCopy($sPackagePath, $bUseFtp);
  198. @bx_rrmdir($sPackagePath);
  199. if($mixedResult !== true)
  200. return $mixedResult;
  201. return true;
  202. }
  203. protected function performWrite($aItem, &$sFilePath)
  204. {
  205. $sFilePath = BX_DIRECTORY_PATH_TMP . $aItem['name'];
  206. if(file_exists($sFilePath))
  207. @unlink($sFilePath);
  208. if(!$rHandler = fopen($sFilePath, 'w'))
  209. return _t('_adm_str_err_cannot_write');
  210. if(!fwrite($rHandler, urldecode($aItem['content'])))
  211. return _t('_adm_str_err_cannot_write');
  212. fclose($rHandler);
  213. return true;
  214. }
  215. protected function performUnarchive($sFilePath, &$sPackagePath)
  216. {
  217. if(!class_exists('ZipArchive'))
  218. return _t('_adm_str_err_zip_not_available');
  219. $oZip = new ZipArchive();
  220. if($oZip->open($sFilePath) !== true)
  221. return _t('_adm_str_err_cannot_unzip_package');
  222. $sPackageFolder = '';
  223. if($oZip->numFiles > 0)
  224. $sPackageFolder = $oZip->getNameIndex(0);
  225. if(empty($sPackageFolder))
  226. return _t('_adm_str_err_cannot_unzip_package');
  227. $sPackagePath = BX_DIRECTORY_PATH_TMP . $sPackageFolder;
  228. if(file_exists($sPackagePath)) // remove existing tmp folder with the same name
  229. @bx_rrmdir($sPackagePath);
  230. if(!$oZip->extractTo(BX_DIRECTORY_PATH_TMP))
  231. return _t('_adm_str_err_cannot_unzip_package');
  232. $oZip->close();
  233. return true;
  234. }
  235. protected function performCopy($sPackagePath, $bUseFtp)
  236. {
  237. if($bUseFtp) {
  238. $sLogin = getParam('sys_ftp_login');
  239. $sPassword = getParam('sys_ftp_password');
  240. $sPath = getParam('sys_ftp_dir');
  241. if(empty($sLogin) || empty($sPassword) || empty($sPath))
  242. return _t('_adm_str_err_no_ftp_info');
  243. bx_import('BxDolFtp');
  244. $oFile = new BxDolFtp($_SERVER['HTTP_HOST'], $sLogin, $sPassword, $sPath);
  245. if(!$oFile->connect())
  246. return _t('_adm_str_err_cannot_connect_to_ftp');
  247. if(!$oFile->isDolphin())
  248. return _t('_adm_str_err_destination_not_valid');
  249. }
  250. else {
  251. bx_import('BxDolFile');
  252. $oFile = BxDolFile::getInstance();
  253. }
  254. $aConfig = self::getModuleConfig($sPackagePath . 'install/config.php');
  255. if(empty($aConfig) || !is_array($aConfig) || empty($aConfig['home_dir']))
  256. return _t('_adm_str_err_wrong_package_format');
  257. if(!$oFile->copy($sPackagePath, 'modules/' . $aConfig['home_dir']))
  258. return _t('_adm_str_err_files_copy_failed');
  259. return true;
  260. }
  261. protected function performAction($sDirectory, $sOperation, $aParams = array())
  262. {
  263. $sConfigFile = 'install/config.php';
  264. $sInstallerFile = 'install/installer.php';
  265. $sInstallerClass = $sOperation == 'update' ? 'Updater' : 'Installer';
  266. $aConfig = self::getModuleConfig(BX_DIRECTORY_PATH_MODULES . $sDirectory . $sConfigFile);
  267. $sPathInstaller = BX_DIRECTORY_PATH_MODULES . $sDirectory . $sInstallerFile;
  268. if(empty($aConfig) || !file_exists($sPathInstaller))
  269. return array(
  270. 'code' => 2,
  271. 'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory)
  272. );
  273. require_once($sPathInstaller);
  274. $sClassName = $aConfig['class_prefix'] . $sInstallerClass;
  275. $oInstaller = new $sClassName($aConfig);
  276. $aResult = $oInstaller->$sOperation($aParams);
  277. return array(
  278. 'code' => $aResult['result'] ? 0 : 2,
  279. 'message' => $aResult['message']
  280. );
  281. }
  282. protected function getConfigModule($sConfigPath, $aInstalledPathes = array(), $aInstalledInfo = array())
  283. {
  284. $aConfig = self::getModuleConfig($sConfigPath);
  285. if(empty($aConfig) || !is_array($aConfig))
  286. return array();
  287. $sModulePath = $aConfig['home_dir'];
  288. $bInstalled = !empty($aInstalledPathes) && in_array($sModulePath, $aInstalledPathes);
  289. $bEnabled = $bInstalled && !empty($aInstalledInfo) && (int)$aInstalledInfo[$sModulePath]['enabled'] == 1;
  290. return array(
  291. 'type' => $aConfig['type'],
  292. 'name' => isset($aConfig['name']) ? $aConfig['name'] : $aConfig['home_uri'],
  293. 'title' => bx_process_output($aConfig['title']),
  294. 'vendor' => $aConfig['vendor'],
  295. 'version' => $aConfig['version'],
  296. 'uri' => $aConfig['home_uri'],
  297. 'dir' => $aConfig['home_dir'],
  298. 'note' => isset($aConfig['note']) ? bx_process_output($aConfig['note']) : '',
  299. 'installed' => $bInstalled,
  300. 'enabled' => $bInstalled && $bEnabled
  301. );
  302. }
  303. protected function getConfigUpdate($sConfigPathModule, $sConfigPathUpdate, $aInstalledPathes = array(), $aInstalledInfo = array())
  304. {
  305. $aModule = $this->getConfigModule($sConfigPathModule, $aInstalledPathes, $aInstalledInfo);
  306. if(empty($aModule) || !$aModule['installed'])
  307. return array();
  308. $aConfig = self::getModuleConfig($sConfigPathUpdate);
  309. if(empty($aConfig) || !is_array($aConfig))
  310. return array();
  311. return array(
  312. 'title' => bx_process_output($aConfig['title']),
  313. 'vendor' => $aConfig['vendor'],
  314. 'version_from' => $aConfig['version_from'],
  315. 'version_to' => $aConfig['version_to'],
  316. 'dir' => $aConfig['home_dir'],
  317. 'module_type' => $aModule['type'],
  318. 'module_name' => $aModule['name'],
  319. 'module_dir' => $aModule['dir']
  320. );
  321. }
  322. private function getInstalledInfo(&$aInstalledPathes, &$aInstalledInfo)
  323. {
  324. bx_import('BxDolModuleDb');
  325. $aModules = BxDolModuleDb::getInstance()->getModules();
  326. $aInstalledInfo = array();
  327. foreach($aModules as $aModule)
  328. $aInstalledInfo[$aModule['path']] = $aModule;
  329. $aInstalledPathes = array_keys($aInstalledInfo);
  330. }
  331. }
  332. /** @} */