BxDolStudioInstallerUtils.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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('BxDolLanguages');
  10. bx_import('BxDolInstallerUtils');
  11. define('BX_DOL_UNITY_URL_ROOT', 'http://www.boonex.com/');
  12. define('BX_DOL_UNITY_URL_MARKET', BX_DOL_UNITY_URL_ROOT . 'market/');
  13. class BxDolStudioInstallerUtils extends BxDolInstallerUtils implements iBxDolSingleton
  14. {
  15. public function __construct()
  16. {
  17. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  18. trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
  19. parent::__construct();
  20. }
  21. /**
  22. * Prevent cloning the instance
  23. */
  24. public function __clone()
  25. {
  26. if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
  27. trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
  28. }
  29. /**
  30. * Get singleton instance of the class
  31. */
  32. public static function getInstance()
  33. {
  34. if (!isset($GLOBALS['bxDolClasses'][__CLASS__]))
  35. $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolStudioInstallerUtils();
  36. return $GLOBALS['bxDolClasses'][__CLASS__];
  37. }
  38. public function serviceDownloadFileComplete($sFilePath)
  39. {
  40. return $this->downloadFileComplete($sFilePath);
  41. }
  42. public function servicePerformAction($sDirectory, $sOperation, $aParams)
  43. {
  44. return $this->perform($sDirectory, $sOperation, $aParams);
  45. }
  46. public function getModules($bTitleAsKey = true)
  47. {
  48. $aModules = array();
  49. $oTemplate = BxDolStudioTemplate::getInstance();
  50. $aInstalledInfo = $this->getInstalledInfo();
  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. $sModulePath = $sVendor . '/' . $sModule . '/';
  61. $aModule = $this->getConfigModule($sPath . $sModulePath . 'install/config.php', !empty($aInstalledInfo[$sModulePath]) ? $aInstalledInfo[$sModulePath] : array());
  62. if(empty($aModule))
  63. continue;
  64. $aModules[$bTitleAsKey ? $aModule['title'] : $aModule['name']] = $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. $aInstalledInfo = $this->getInstalledInfo();
  80. $sPath = BX_DIRECTORY_PATH_MODULES;
  81. if(($rHandleVendor = opendir($sPath)) !== false) {
  82. while(($sVendor = readdir($rHandleVendor)) !== false) {
  83. if(substr($sVendor, 0, 1) == '.' || !is_dir($sPath . $sVendor))
  84. continue;
  85. if(($rHandleModule = opendir($sPath . $sVendor . '/')) !== false) {
  86. while(($sModule = readdir($rHandleModule)) !== false) {
  87. if(!is_dir($sPath . $sVendor . '/' . $sModule) || substr($sModule, 0, 1) == '.')
  88. continue;
  89. $sModulePath = $sVendor . '/' . $sModule . '/';
  90. if(empty($aInstalledInfo[$sModulePath]))
  91. continue;
  92. $aUpdate = $this->getUpdate($aInstalledInfo[$sModulePath]);
  93. if(!empty($aUpdate))
  94. $aUpdates[$aUpdate['title']] = $aUpdate;
  95. }
  96. closedir($rHandleModule);
  97. }
  98. }
  99. closedir($rHandleVendor);
  100. }
  101. ksort($aUpdates);
  102. return $aUpdates;
  103. }
  104. public function getUpdate($mixedModule)
  105. {
  106. $aResult = array();
  107. if(!is_array($mixedModule)) {
  108. bx_import('BxDolModuleQuery');
  109. $mixedModule = BxDolModuleQuery::getInstance()->{is_numeric($mixedModule) ? 'getModuleById' : 'getModuleByName'}($mixedModule);
  110. }
  111. if(empty($mixedModule) || !is_array($mixedModule))
  112. return $aResult;
  113. $sPathModule = BX_DIRECTORY_PATH_MODULES . $mixedModule['path'];
  114. $sPathUpdates = $sPathModule . 'updates/';
  115. $rHandleUpdate = @opendir($sPathUpdates);
  116. if($rHandleUpdate === false)
  117. return $aResult;
  118. while(($sUpdate = readdir($rHandleUpdate)) !== false) {
  119. $sPathUpdate = $sPathUpdates . $sUpdate . '/';
  120. if(substr($sUpdate, 0, 1) == '.' || !is_dir($sPathUpdate))
  121. continue;
  122. $aUpdate = $this->getConfigUpdate($sPathModule . 'install/config.php', $sPathUpdate . 'install/config.php', $mixedModule);
  123. if(empty($aUpdate) || !is_array($aUpdate) || version_compare($aUpdate['module_version'], $aUpdate['version_from']) != 0)
  124. continue;
  125. $aResult = $aUpdate;
  126. break;
  127. }
  128. closedir($rHandleUpdate);
  129. return $aResult;
  130. }
  131. public function perform($sDirectory, $sOperation, $aParams = array())
  132. {
  133. if(!defined('BX_DOL_CRON_EXECUTE') && !self::isRealOwner() && !in_array($sOperation, array('install', 'uninstall', 'enable', 'disable'))) {
  134. if($this->addTransientJob('perform_action', array($sDirectory, $sOperation, $aParams)))
  135. return array('code' => 1, 'message' => _t('_adm_mod_msg_process_operation_scheduled'));
  136. else
  137. return array('code' => 2, 'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory));
  138. }
  139. $sConfigFile = 'install/config.php';
  140. $sInstallerFile = 'install/installer.php';
  141. $sInstallerClass = $sOperation == 'update' ? 'Updater' : 'Installer';
  142. $aConfig = self::getModuleConfig(BX_DIRECTORY_PATH_MODULES . $sDirectory . $sConfigFile);
  143. $sPathInstaller = BX_DIRECTORY_PATH_MODULES . $sDirectory . $sInstallerFile;
  144. if(empty($aConfig) || !file_exists($sPathInstaller))
  145. return array('code' => 2, 'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory));
  146. require_once($sPathInstaller);
  147. $sClassName = $aConfig['class_prefix'] . $sInstallerClass;
  148. $oInstaller = new $sClassName($aConfig);
  149. $aResult = $oInstaller->$sOperation($aParams);
  150. return array('code' => $aResult['result'] ? 0 : 2, 'message' => $aResult['message']);
  151. }
  152. public function checkModules($bAuthorizedAccess = false)
  153. {
  154. if($bAuthorizedAccess) {
  155. bx_import('BxDolStudioOAuth');
  156. $aProducts = BxDolStudioOAuth::getInstance()->loadItems(array('dol_type' => 'purchased_products', 'dol_domain' => BX_DOL_URL_ROOT));
  157. }
  158. else {
  159. bx_import('BxDolStudioJson');
  160. $aProducts = BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_purchased', array(
  161. 'key' => getParam('sys_oauth_key')
  162. ));
  163. }
  164. bx_import('BxDolModuleQuery');
  165. $oModuleDb = BxDolModuleQuery::getInstance();
  166. $oModuleDb->updateModule(array('hash' => ''));
  167. if(!empty($aProducts) && is_array($aProducts))
  168. foreach ($aProducts as $aProduct)
  169. $oModuleDb->updateModule(array('hash' => $aProduct['hash']), array('name' => $aProduct['name']));
  170. return $aProducts;
  171. }
  172. public function checkUpdates($bAuthorizedAccess = false)
  173. {
  174. bx_import('BxDolModuleQuery');
  175. $aModules = BxDolModuleQuery::getInstance()->getModules();
  176. $aProducts = array();
  177. foreach($aModules as $aModule) {
  178. if(empty($aModule['name']))
  179. continue;
  180. $aProducts[] = array(
  181. 'name' => $aModule['name'],
  182. 'version' => $aModule['version'],
  183. 'hash' => $aModule['hash'],
  184. );
  185. }
  186. $sProducts = base64_encode(serialize($aProducts));
  187. if($bAuthorizedAccess) {
  188. bx_import('BxDolStudioOAuth');
  189. return BxDolStudioOAuth::getInstance()->loadItems(array('dol_type' => 'available_updates', 'dol_products' => $sProducts));
  190. }
  191. bx_import('BxDolStudioJson');
  192. return BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_updates', array(
  193. 'key' => getParam('sys_oauth_key'),
  194. 'products' => $sProducts
  195. ));
  196. }
  197. public function downloadFileAuthorized($iFileId)
  198. {
  199. bx_import('BxDolStudioOAuth');
  200. $aItem = BxDolStudioOAuth::getInstance()->loadItems(array('dol_type' => 'product_file', 'dol_file_id' => $iFileId));
  201. return $this->downloadFileInit($aItem);
  202. }
  203. public function downloadUpdatePublic($sModuleName)
  204. {
  205. bx_import('BxDolModuleQuery');
  206. $aModule = BxDolModuleQuery::getInstance()->getModuleByName($sModuleName);
  207. bx_import('BxDolStudioJson');
  208. $aItem = BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_download_update', array(
  209. 'key' => getParam('sys_oauth_key'),
  210. 'product' => base64_encode(serialize(array(
  211. 'name' => $aModule['name'],
  212. 'version' => $aModule['version'],
  213. 'hash' => $aModule['hash'],
  214. ))),
  215. ));
  216. return $this->downloadFileInit($aItem);
  217. }
  218. protected function downloadFileInit($aItem)
  219. {
  220. if(empty($aItem) || !is_array($aItem))
  221. return $aItem;
  222. //--- write ZIP archive.
  223. $sFilePath = '';
  224. $mixedResult = $this->performWrite($aItem, $sFilePath);
  225. if($mixedResult !== true)
  226. return $mixedResult;
  227. return $this->downloadFileComplete($sFilePath);
  228. }
  229. protected function downloadFileComplete($sFilePath, $bUseFtp = BX_FORCE_USE_FTP_FILE_TRANSFER)
  230. {
  231. if(!defined('BX_DOL_CRON_EXECUTE') && !self::isRealOwner())
  232. return _t($this->addTransientJob('download_file_complete', array($sFilePath)) ? '_adm_str_msg_download_scheduled' : '_adm_str_err_download_failed');
  233. //--- Unarchive package.
  234. $sPackagePath = '';
  235. $mixedResult = $this->performUnarchive($sFilePath, $sPackagePath);
  236. @unlink($sFilePath);
  237. if($mixedResult !== true)
  238. return $mixedResult;
  239. //--- Copy unarchived package.
  240. $mixedResult = $this->performCopy($sPackagePath, $bUseFtp);
  241. @bx_rrmdir($sPackagePath);
  242. if($mixedResult !== true)
  243. return $mixedResult;
  244. return true;
  245. }
  246. protected function performWrite($aItem, &$sFilePath)
  247. {
  248. $iUmaskSave = umask(0);
  249. $sFilePath = BX_DIRECTORY_PATH_TMP . $aItem['name'];
  250. if(file_exists($sFilePath))
  251. @unlink($sFilePath);
  252. if(!$rHandler = fopen($sFilePath, 'w'))
  253. return _t('_adm_str_err_cannot_write');
  254. $sContent = urldecode($aItem['content']);
  255. if(!fwrite($rHandler, $sContent, strlen($sContent)))
  256. return _t('_adm_str_err_cannot_write');
  257. fclose($rHandler);
  258. umask($iUmaskSave);
  259. return true;
  260. }
  261. protected function performUnarchive($sFilePath, &$sPackagePath)
  262. {
  263. $iUmaskSave = umask(0);
  264. if(!class_exists('ZipArchive'))
  265. return _t('_adm_str_err_zip_not_available');
  266. $oZip = new ZipArchive();
  267. if($oZip->open($sFilePath) !== true)
  268. return _t('_adm_str_err_cannot_unzip_package');
  269. $sPackageFolder = '';
  270. if($oZip->numFiles > 0)
  271. $sPackageFolder = $oZip->getNameIndex(0);
  272. if(empty($sPackageFolder))
  273. return _t('_adm_str_err_cannot_unzip_package');
  274. $sPackagePath = BX_DIRECTORY_PATH_TMP . $sPackageFolder;
  275. if(file_exists($sPackagePath)) // remove existing tmp folder with the same name
  276. @bx_rrmdir($sPackagePath);
  277. if(!$oZip->extractTo(BX_DIRECTORY_PATH_TMP))
  278. return _t('_adm_str_err_cannot_unzip_package');
  279. $oZip->close();
  280. umask($iUmaskSave);
  281. return true;
  282. }
  283. protected function performCopy($sPackagePath, $bUseFtp)
  284. {
  285. if($bUseFtp) {
  286. $sLogin = getParam('sys_ftp_login');
  287. $sPassword = getParam('sys_ftp_password');
  288. $sPath = getParam('sys_ftp_dir');
  289. if(empty($sLogin) || empty($sPassword) || empty($sPath))
  290. return _t('_adm_str_err_no_ftp_info');
  291. bx_import('BxDolFtp');
  292. $oFile = new BxDolFtp($_SERVER['HTTP_HOST'], $sLogin, $sPassword, $sPath);
  293. if(!$oFile->connect())
  294. return _t('_adm_str_err_cannot_connect_to_ftp');
  295. if(!$oFile->isDolphin())
  296. return _t('_adm_str_err_destination_not_valid');
  297. }
  298. else {
  299. bx_import('BxDolFile');
  300. $oFile = BxDolFile::getInstance();
  301. }
  302. $aConfig = self::getModuleConfig($sPackagePath . 'install/config.php');
  303. if(empty($aConfig) || !is_array($aConfig) || empty($aConfig['home_dir']))
  304. return _t('_adm_str_err_wrong_package_format');
  305. if(!$oFile->copy($sPackagePath, 'modules/' . $aConfig['home_dir']))
  306. return _t('_adm_str_err_files_copy_failed');
  307. return true;
  308. }
  309. protected function getConfigModule($sConfigPath, $aInstalled = array())
  310. {
  311. $aConfig = self::getModuleConfig($sConfigPath);
  312. if(empty($aConfig) || !is_array($aConfig))
  313. return array();
  314. $bInstalled = !empty($aInstalled);
  315. $bEnabled = $bInstalled && (int)$aInstalled['enabled'] == 1;
  316. $sVersion = $bInstalled ? $aInstalled['version'] : $aConfig['version'];
  317. return array(
  318. 'type' => $aConfig['type'],
  319. 'name' => isset($aConfig['name']) ? $aConfig['name'] : $aConfig['home_uri'],
  320. 'title' => bx_process_output($aConfig['title']),
  321. 'vendor' => $aConfig['vendor'],
  322. 'version' => $sVersion,
  323. 'uri' => $aConfig['home_uri'],
  324. 'dir' => $aConfig['home_dir'],
  325. 'note' => isset($aConfig['note']) ? bx_process_output($aConfig['note']) : '',
  326. 'installed' => $bInstalled,
  327. 'enabled' => $bEnabled
  328. );
  329. }
  330. protected function getConfigUpdate($sConfigPathModule, $sConfigPathUpdate, $aInstalledModule = array())
  331. {
  332. $aModule = $this->getConfigModule($sConfigPathModule, $aInstalledModule);
  333. if(empty($aModule) || !$aModule['installed'])
  334. return array();
  335. $aConfig = self::getModuleConfig($sConfigPathUpdate);
  336. if(empty($aConfig) || !is_array($aConfig))
  337. return array();
  338. return array(
  339. 'title' => bx_process_output($aConfig['title']),
  340. 'vendor' => $aConfig['vendor'],
  341. 'version_from' => $aConfig['version_from'],
  342. 'version_to' => $aConfig['version_to'],
  343. 'dir' => $aConfig['home_dir'],
  344. 'module_type' => $aModule['type'],
  345. 'module_name' => $aModule['name'],
  346. 'module_dir' => $aModule['dir'],
  347. 'module_version' => $aModule['version']
  348. );
  349. }
  350. private function getInstalledInfo()
  351. {
  352. bx_import('BxDolModuleQuery');
  353. $aModules = BxDolModuleQuery::getInstance()->getModules();
  354. $aInstalledInfo = array();
  355. foreach($aModules as $aModule)
  356. $aInstalledInfo[$aModule['path']] = $aModule;
  357. return $aInstalledInfo;
  358. }
  359. private function addTransientJob($sAction, $aParams)
  360. {
  361. bx_import('BxDolCronQuery');
  362. if(BxDolCronQuery::getInstance()->addTransientJobService('sys_' . $sAction, array('system', $sAction, $aParams, 'DolStudioInstallerUtils')))
  363. return true;
  364. return false;
  365. }
  366. }
  367. /** @} */