Guest User

core_update_model

a guest
Aug 24th, 2018
385
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.66 KB | None | 0 0
  1. <?php
  2.  
  3. if (!defined('BASEPATH')) {
  4. exit('No direct script access allowed');
  5. }
  6.  
  7. /**
  8. *
  9. * NEOU CMS v4 CI Edition
  10. * @copyright (c) 2018, Alexander Fagard
  11. * @requires PHP version >= 5.6
  12. *
  13. * You cannot redistribute this document without written permission from the author
  14. *
  15. */
  16. use Cake\Filesystem\File;
  17. use Cake\Filesystem\Folder;
  18.  
  19. class Core_update_model extends CI_Model {
  20.  
  21. private $folders = array(
  22. 'application/controllers/' . CMS_DIR_NAME,
  23. 'application/helpers',
  24. 'application/language',
  25. 'application/libraries',
  26. 'application/models/backend',
  27. 'application/views/backend',
  28. 'application/core',
  29. 'application/third_party',
  30. 'application/hooks'
  31. );
  32. private $excluded_files = array(
  33. 'MY_Frontend.php',
  34. 'Config.php'
  35. );
  36. private $limit_files_per_open = true;
  37. // do not edit
  38. public $files_processed = 0;
  39. public $updates_folder;
  40. private $dir_unpack;
  41. private $max_files_per_open = 1024;
  42. private $current_file_count = 0;
  43. private $zip;
  44. private $manifest = array();
  45. private $manifest_file = 'MANIFEST.txt';
  46. private $identifier_file = 'SYSUPDATE.txt';
  47.  
  48. public function __construct() {
  49. parent::__construct();
  50. $this->lang->load('core_update');
  51. $this->updates_folder = genpath(APPPATH . 'migrations/system_updates/');
  52. $this->dir_unpack = genpath();
  53. if (!extension_loaded('zip')) {
  54. show_error(sprintf($this->lang->line('update_zip_ext_failed_load'), __CLASS__), 500);
  55. } else {
  56. $this->zip = new \ZipArchive;
  57. }
  58. }
  59.  
  60. /**
  61. * Restores version before update
  62. * Deletes new files that were added from the update
  63. *
  64. * @throws Exception
  65. * @return void
  66. */
  67. public function restore_previous() {
  68. try {
  69. $beforeupdate = $this->find_previous_version();
  70. $new_manifest_obj = new File($this->updates_folder . 'manifests/manifest_new.txt');
  71. $old_manifest_obj = new File($this->updates_folder . 'manifests/manifest_old.txt');
  72. $new_manifest = $new_manifest_obj->read();
  73. $old_manifest = $old_manifest_obj->read();
  74. if ($new_manifest === false || $old_manifest === false) {
  75. throw new Exception($this->lang->line('update_manifest_error'));
  76. }
  77. $new_manifest = unserialize($new_manifest);
  78. $old_manifest = unserialize($old_manifest);
  79. $difference = array_diff($new_manifest, $old_manifest);
  80. if (count($difference) > 0) {
  81. foreach ($difference as $file) {
  82. @unlink($this->dir_unpack . $file); // remove new files
  83. }
  84. }
  85. if ($this->zip->open($beforeupdate->pwd()) !== true) {
  86. throw new Exception(sprintf($this->lang->line('update_zip_failed_to_open'), __FUNCTION__, $beforeupdate->pwd()));
  87. }
  88. $this->verify_update($this->zip, $beforeupdate->name()); // name() w/o extension
  89. $this->files_processed = $this->zip->numFiles;
  90. $this->zip->extractTo($this->dir_unpack);
  91. $this->zip->close();
  92. rmdir_recursive($this->updates_folder);
  93. } catch (Exception $e) {
  94. @$this->zip->close();
  95. throw $e;
  96. } finally {
  97. @unlink($this->dir_unpack . $this->identifier_file);
  98. @unlink($this->dir_unpack . $this->manifest_file);
  99. }
  100. }
  101.  
  102. /**
  103. * Checks to make sure update is valid
  104. * If so, extracts updated files to the main directory
  105. * BEFORE
  106. * Deletes old updates
  107. * (making sure only current update file and image to be made exists)
  108. * Creates an image of the site as it is
  109. *
  110. * @param string $file Path to update file
  111. * @return void
  112. */
  113. public function apply_update($file) {
  114. try {
  115. $file = new File($file);
  116. if (!$file->exists()) {
  117. throw new Exception(sprintf($this->lang->line('update_file_not_exist'), $file->pwd()));
  118. }
  119. $zip = new \ZipArchive;
  120. if ($zip->open($file->pwd()) !== true) {
  121. throw new Exception(sprintf($this->lang->line('update_zip_failed_to_open'), __FUNCTION__, $file->pwd()));
  122. }
  123. $this->verify_update($zip, $file->name()); // name() w/o extension
  124. $this->delete_old_updates($file->name); // name w/ extension
  125. $this->create_update($file->name() . '_beforeupdate'); // image of site before update
  126. $this->files_processed = $zip->numFiles;
  127. $zip->extractTo($this->dir_unpack);
  128. @unlink($this->dir_unpack . $this->identifier_file);
  129. if (!rename($this->dir_unpack . $this->manifest_file, $this->updates_folder . 'manifests' . DS . 'manifest_new.txt')) {
  130. throw new Exception($this->lang->line('update_manifest_move_error'));
  131. }
  132. $zip->close();
  133. } catch (\Exception $e) {
  134. $zip->close();
  135. throw $e;
  136. }
  137. }
  138.  
  139. /**
  140. * Creates an update ZIP with included folders and identifier
  141. * Adds a manifest of updated files
  142. *
  143. * @param string $update_name
  144. * @throws Exception
  145. * @return void
  146. */
  147. public function create_update($update_name = null) {
  148. if (is_null($update_name)) {
  149. $update_name = 'sysupdate_' . date('Ymd_Hms');
  150. }
  151. $destination = $this->updates_folder . $update_name . '.zip';
  152. try {
  153. $this->mk_update_dir();
  154. if ($this->zip->open($destination, \ZIPARCHIVE::CREATE | \ZIPARCHIVE::OVERWRITE) !== true) {
  155. throw new Exception(sprintf($this->lang->line('update_zip_failed_to_create'), __FUNCTION__, $destination));
  156. }
  157. $this->identify_update($update_name);
  158. $dirs = @array_map(array($this, 'resolve_folder_path'), $this->folders);
  159. foreach ($dirs as $dir) {
  160. $this->zippy($dir, $destination);
  161. }
  162. $this->create_manifest();
  163. $this->zip->close();
  164. } catch (\Exception $e) {
  165. @$this->zip->close();
  166. @unlink($destination); // zip has to be closed before we remove the $destination
  167. throw $e; // rethrow
  168. }
  169. }
  170.  
  171. /**
  172. * Checks to see if previous version exists
  173. *
  174. * @return boolean
  175. */
  176. public function previous_exists() {
  177. try {
  178. $this->find_previous_version();
  179. return true;
  180. } catch (Exception $e) {
  181. return false;
  182. }
  183. }
  184.  
  185. /**
  186. * Finds the previous version by _beforeupdate
  187. *
  188. * @return \File
  189. * @throws Exception
  190. */
  191. private function find_previous_version() {
  192. $files = (new Folder($this->updates_folder))->find('.*\_beforeupdate.zip', true);
  193. $count = count($files);
  194. if ($count > 1) {
  195. throw new Exception($this->lang->line('update_previous_error_1'));
  196. } elseif ($count < 1) {
  197. throw new Exception($this->lang->line('update_previous_error_2'));
  198. } else {
  199. return new File($this->updates_folder . $files[0]);
  200. }
  201. }
  202.  
  203. /**
  204. * Keeps only current file
  205. *
  206. * @param string $current_file Name of current file with ext
  207. * @return void
  208. */
  209. private function delete_old_updates($current_file) {
  210. $files = dir_files_array($this->updates_folder);
  211. foreach ($files as $file) {
  212. if ($file == $current_file) {
  213. continue;
  214. }
  215. @unlink($this->updates_folder . $file);
  216. }
  217. }
  218.  
  219. /**
  220. * Creates a manifest/array of files that were added to zip
  221. *
  222. * @throws Exception
  223. * @return void
  224. */
  225. private function create_manifest() {
  226. $manifest = new File($this->updates_folder . 'manifests' . DS . 'manifest_old.txt', true);
  227. $contents = serialize($this->manifest);
  228. if (!$manifest->write($contents)) {
  229. throw new Exception($this->lang->line('update_manifest_write_error'));
  230. }
  231. $manifest->close();
  232. if ($this->zip->addFile($manifest->pwd(), $this->manifest_file)) {
  233. //$this->files_processed++;
  234. $this->current_file_count++;
  235. } else {
  236. @unlink($manifest->pwd());
  237. throw new Exception($this->lang->line('update_manifest_add_error'));
  238. }
  239. }
  240.  
  241. /**
  242. * Adds an identifier file that is base64 encoded
  243. * version of the filename
  244. *
  245. * @param string $update_name
  246. * @throws Exception
  247. * @return void
  248. */
  249. private function identify_update($update_name) {
  250. if ($this->zip->addFromString($this->identifier_file, base64_encode($update_name))) {
  251. //$this->files_processed++;
  252. $this->current_file_count++;
  253. } else {
  254. throw new Exception($this->lang->line('update_id_add_error'));
  255. }
  256. }
  257.  
  258. /**
  259. * Checks to make sure identifier file exists and base64 decodes to
  260. * match the filename
  261. *
  262. * Checks to make sure that the manifest file exists
  263. *
  264. * @param object $zip
  265. * @param type $update_name
  266. * @throws Exception
  267. * @return void
  268. */
  269. private function verify_update($zip, $update_name) {
  270. $identifier = $zip->getFromName($this->identifier_file);
  271. if (!$identifier || base64_decode($identifier) !== $update_name) {
  272. throw new Exception($this->lang->line('update_id_find_error'));
  273. }
  274. if (!$zip->getFromName($this->manifest_file)) {
  275. throw new Exception($this->lang->line('update_manifest_find_error'));
  276. }
  277. }
  278.  
  279. /**
  280. * Creates update folder if not exists
  281. *
  282. * @throws Exception
  283. */
  284. public function mk_update_dir() {
  285. if (!is_dir($this->updates_folder) && mkdir($this->updates_folder, DIR_WRITE_MODE) === false) {
  286. throw new Exception(sprintf($this->lang->line('update_create_dir_error'), $this->updates_folder));
  287. }
  288. }
  289.  
  290. /**
  291. * Resolves folder path
  292. *
  293. * @param string $folder
  294. * @return string $path Revised folder path
  295. * @throws Exception
  296. */
  297. private function resolve_folder_path($folder) {
  298. $path = fix_slashes($this->dir_unpack . Folder::slashTerm($folder));
  299. if (is_dir($path)) {
  300. return $path;
  301. } else {
  302. throw new Exception(sprintf($this->lang->line('update_dir_not_exist'), $path));
  303. }
  304. }
  305.  
  306. /**
  307. * Creates a zip file from a single $source directory
  308. * All added files get added to the manifest array
  309. *
  310. * @param string $dir (path)
  311. * @throws Exception
  312. * @return void
  313. */
  314. private function zippy($dir, $destination) {
  315. $files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::SELF_FIRST);
  316. foreach ($files as $file) {
  317. if ($file->isDir()) {
  318. continue;
  319. }
  320. if ($file->isFile()) {
  321. $filename = $file->getBasename();
  322. if (in_array($filename, $this->excluded_files)) {
  323. // ignored files
  324. continue;
  325. } else {
  326. $this->current_file_count++; // increment curr file count
  327. // add file to zip
  328. if ($this->limit_files_per_open) {
  329. $this->reopen($destination);
  330. }
  331. $path = str_replace($this->dir_unpack, '', $file);
  332. $path = ltrim(fix_slashes($path, 'linux'), '/');
  333. $this->zip->addFile($file, $path); // add file
  334. $this->manifest[] = $path;
  335. $this->files_processed++; // increment files added
  336. }
  337. }
  338. }
  339. }
  340.  
  341. /**
  342. * Reopens zip file $destination and resets current file count
  343. *
  344. * @param string $destination (path)
  345. * @throws Exception
  346. * @return void
  347. */
  348. private function reopen($destination) {
  349. if ($this->current_file_count >= $this->max_files_per_open) {
  350. $this->zip->close();
  351. $this->current_file_count = 0; // reset curr file count
  352. if ($this->zip->open($destination, \ZIPARCHIVE::CREATE) !== true) {
  353. throw new Exception(sprintf($this->lang->line('update_zip_reopen_fail'), __FUNCTION__, $destination, $this->files_processed));
  354. }
  355. }
  356. }
  357.  
  358. }
Add Comment
Please, Sign In to add comment