PageRenderTime 58ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/httpdocs/wp-content/plugins/worker/backup.class.php

https://bitbucket.org/vinnytroia/curvve-media
PHP | 3260 lines | 2217 code | 366 blank | 677 comment | 518 complexity | e08f2907f3248351fd2aaa3a810e8175 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-3.0, LGPL-2.1, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*************************************************************
  3. *
  4. * backup.class.php
  5. *
  6. * Manage Backups
  7. *
  8. *
  9. * Copyright (c) 2011 Prelovac Media
  10. * www.prelovac.com
  11. **************************************************************/
  12. if(basename($_SERVER['SCRIPT_FILENAME']) == "backup.class.php"):
  13. echo "Sorry but you cannot browse this file directly!";
  14. exit;
  15. endif;
  16. define('MWP_BACKUP_DIR', WP_CONTENT_DIR . '/managewp/backups');
  17. define('MWP_DB_DIR', MWP_BACKUP_DIR . '/mwp_db');
  18. $zip_errors = array(
  19. 'No error',
  20. 'No error',
  21. 'Unexpected end of zip file',
  22. 'A generic error in the zipfile format was detected.',
  23. 'zip was unable to allocate itself memory',
  24. 'A severe error in the zipfile format was detected',
  25. 'Entry too large to be split with zipsplit',
  26. 'Invalid comment format',
  27. 'zip -T failed or out of memory',
  28. 'The user aborted zip prematurely',
  29. 'zip encountered an error while using a temp file',
  30. 'Read or seek error',
  31. 'zip has nothing to do',
  32. 'Missing or empty zip file',
  33. 'Error writing to a file',
  34. 'zip was unable to create a file to write to',
  35. 'bad command line parameters',
  36. 'no error',
  37. 'zip could not open a specified file to read'
  38. );
  39. $unzip_errors = array(
  40. 'No error',
  41. 'One or more warning errors were encountered, but processing completed successfully anyway',
  42. 'A generic error in the zipfile format was detected',
  43. 'A severe error in the zipfile format was detected.',
  44. 'unzip was unable to allocate itself memory.',
  45. 'unzip was unable to allocate memory, or encountered an encryption error',
  46. 'unzip was unable to allocate memory during decompression to disk',
  47. 'unzip was unable allocate memory during in-memory decompression',
  48. 'unused',
  49. 'The specified zipfiles were not found',
  50. 'Bad command line parameters',
  51. 'No matching files were found',
  52. 50 => 'The disk is (or was) full during extraction',
  53. 51 => 'The end of the ZIP archive was encountered prematurely.',
  54. 80 => 'The user aborted unzip prematurely.',
  55. 81 => 'Testing or extraction of one or more files failed due to unsupported compression methods or unsupported decryption.',
  56. 82 => 'No files were found due to bad decryption password(s)'
  57. );
  58. /**
  59. * The main class for processing database and full backups on ManageWP worker.
  60. *
  61. * @copyright 2011-2012 Prelovac Media
  62. * @version 3.9.24
  63. * @package ManageWP
  64. * @subpackage backup
  65. *
  66. */
  67. class MMB_Backup extends MMB_Core {
  68. var $site_name;
  69. var $statuses;
  70. var $tasks;
  71. var $s3;
  72. var $ftp;
  73. var $dropbox;
  74. var $google_drive;
  75. /**
  76. * Initializes site_name, statuses, and tasks attributes.
  77. *
  78. * @return void
  79. */
  80. function __construct() {
  81. parent::__construct();
  82. $this->site_name = str_replace(array(
  83. "_",
  84. "/",
  85. "~"
  86. ), array(
  87. "",
  88. "-",
  89. "-"
  90. ), rtrim($this->remove_http(get_bloginfo('url')), "/"));
  91. $this->statuses = array(
  92. 'db_dump' => 1,
  93. 'db_zip' => 2,
  94. 'files_zip' => 3,
  95. 's3' => 4,
  96. 'dropbox' => 5,
  97. 'ftp' => 6,
  98. 'email' => 7,
  99. 'google_drive' => 8,
  100. 'finished' => 100
  101. );
  102. $this->tasks = get_option('mwp_backup_tasks');
  103. }
  104. /**
  105. * Tries to increase memory limit to 384M and execution time to 600s.
  106. *
  107. * @return array an array with two keys for execution time and memory limit (0 - if not changed, 1 - if succesfully)
  108. */
  109. function set_memory() {
  110. $changed = array('execution_time' => 0, 'memory_limit' => 0);
  111. $memory_limit = trim(ini_get('memory_limit'));
  112. $last = strtolower(substr($memory_limit, -1));
  113. if($last == 'g')
  114. $memory_limit = ((int) $memory_limit)*1024;
  115. else if($last == 'm')
  116. $memory_limit = (int) $memory_limit;
  117. if($last == 'k')
  118. $memory_limit = ((int) $memory_limit)/1024;
  119. if ( $memory_limit < 384 ) {
  120. @ini_set('memory_limit', '384M');
  121. $changed['memory_limit'] = 1;
  122. }
  123. if ( (int) @ini_get('max_execution_time') < 600 ) {
  124. @set_time_limit(600); //ten minutes
  125. $changed['execution_time'] = 1;
  126. }
  127. return $changed;
  128. }
  129. /**
  130. * Returns backup settings from local database for all tasks
  131. *
  132. * @return mixed|boolean
  133. */
  134. function get_backup_settings() {
  135. $backup_settings = get_option('mwp_backup_tasks');
  136. if (!empty($backup_settings))
  137. return $backup_settings;
  138. else
  139. return false;
  140. }
  141. /**
  142. * Sets backup task defined from master, if task name is "Backup Now" this function fires processing backup.
  143. *
  144. * @param mixed $params parameters sent from master
  145. * @return mixed|boolean $this->tasks variable if success, array with error message if error has ocurred, false if $params are empty
  146. */
  147. function set_backup_task($params) {
  148. //$params => [$task_name, $args, $error]
  149. if (!empty($params)) {
  150. //Make sure backup cron job is set
  151. if (!wp_next_scheduled('mwp_backup_tasks')) {
  152. wp_schedule_event( time(), 'tenminutes', 'mwp_backup_tasks' );
  153. }
  154. extract($params);
  155. //$before = $this->get_backup_settings();
  156. $before = $this->tasks;
  157. if (!$before || empty($before))
  158. $before = array();
  159. if (isset($args['remove'])) {
  160. unset($before[$task_name]);
  161. $return = array(
  162. 'removed' => true
  163. );
  164. } else {
  165. if (isset($params['account_info']) && is_array($params['account_info'])) { //only if sends from master first time(secure data)
  166. $args['account_info'] = $account_info;
  167. }
  168. $before[$task_name]['task_args'] = $args;
  169. if (strlen($args['schedule']))
  170. $before[$task_name]['task_args']['next'] = $this->schedule_next($args['type'], $args['schedule']);
  171. $return = $before[$task_name];
  172. }
  173. //Update with error
  174. if (isset($error)) {
  175. if (is_array($error)) {
  176. $before[$task_name]['task_results'][count($before[$task_name]['task_results']) - 1]['error'] = $error['error'];
  177. } else {
  178. $before[$task_name]['task_results'][count($before[$task_name]['task_results'])]['error'] = $error;
  179. }
  180. }
  181. if (isset($time) && $time) { //set next result time before backup
  182. if (is_array($before[$task_name]['task_results'])) {
  183. $before[$task_name]['task_results'] = array_values($before[$task_name]['task_results']);
  184. }
  185. $before[$task_name]['task_results'][count($before[$task_name]['task_results'])]['time'] = $time;
  186. }
  187. $this->update_tasks($before);
  188. //update_option('mwp_backup_tasks', $before);
  189. if ($task_name == 'Backup Now') {
  190. $result = $this->backup($args, $task_name);
  191. $backup_settings = $this->tasks;
  192. if (is_array($result) && array_key_exists('error', $result)) {
  193. $return = $result;
  194. } else {
  195. $return = $backup_settings[$task_name];
  196. }
  197. }
  198. return $return;
  199. }
  200. return false;
  201. }
  202. /**
  203. * Checks if scheduled task is ready for execution,
  204. * if it is ready master sends google_drive_token, failed_emails, success_emails if are needed.
  205. *
  206. * @return void
  207. */
  208. function check_backup_tasks() {
  209. $this->check_cron_remove();
  210. $failed_emails = array();
  211. $settings = $this->tasks;
  212. if (is_array($settings) && !empty($settings)) {
  213. foreach ($settings as $task_name => $setting) {
  214. if ($setting['task_args']['next'] && $setting['task_args']['next'] < time()) {
  215. //if ($setting['task_args']['next'] && $_GET['force_backup']) {
  216. if ($setting['task_args']['url'] && $setting['task_args']['task_id'] && $setting['task_args']['site_key']) {
  217. //Check orphan task
  218. $check_data = array(
  219. 'task_name' => $task_name,
  220. 'task_id' => $setting['task_args']['task_id'],
  221. 'site_key' => $setting['task_args']['site_key'],
  222. 'worker_version' => MMB_WORKER_VERSION
  223. );
  224. if (isset($setting['task_args']['account_info']['mwp_google_drive']['google_drive_token'])) {
  225. $check_data['mwp_google_drive_refresh_token'] = true;
  226. }
  227. $check = $this->validate_task($check_data, $setting['task_args']['url']);
  228. $worker_upto_3_9_22 = (MMB_WORKER_VERSION <= '3.9.22'); // worker version is less or equals to 3.9.22
  229. if ($worker_upto_3_9_22) {
  230. $potential_token = substr($check, 8);
  231. if (substr($check, 0, 8) == 'token - ' && $potential_token != 'not found') {
  232. $this->tasks[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  233. $settings[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  234. $setting['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  235. }
  236. } else {
  237. $potential_token = isset($check['google_drive_token']) ? $check['google_drive_token'] : false;
  238. if ($potential_token) {
  239. $this->tasks[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  240. $settings[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  241. $setting['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  242. }
  243. }
  244. }
  245. $update = array(
  246. 'task_name' => $task_name,
  247. 'args' => $settings[$task_name]['task_args']
  248. );
  249. if($check != 'paused'){
  250. $update['time'] = time();
  251. }
  252. //Update task with next schedule
  253. $this->set_backup_task($update);
  254. if($check == 'paused'){
  255. continue;
  256. }
  257. $result = $this->backup($setting['task_args'], $task_name);
  258. $error = '';
  259. if (is_array($result) && array_key_exists('error', $result)) {
  260. $error = $result;
  261. $this->set_backup_task(array(
  262. 'task_name' => $task_name,
  263. 'args' => $settings[$task_name]['task_args'],
  264. 'error' => $error
  265. ));
  266. } else {
  267. //$setting = $this->tasks[$task_name];
  268. if (@count($setting['task_args']['account_info'])) {
  269. /*$last_result = $setting['task_results'][count($setting['task_results']) - 1];
  270. $backup_file = $last_result['server']['file_path'];
  271. $del_host_file = $setting['task_args']['del_host_file'];*/
  272. wp_schedule_single_event(time(), 'mmb_scheduled_remote_upload', array('args' => array('task_name' => $task_name)));
  273. //spawn_cron(time() + 150);
  274. //wp_remote_post(site_url('index.php'), array( 'timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters( 'https_local_ssl_verify', true ) ));
  275. //update_option('_transient_doing_cron', 0);
  276. /*$nonce = substr(wp_hash(wp_nonce_tick() . 'mmb-backup-nonce' . 0, 'nonce'), -12, 10);
  277. $cron_url = site_url('index.php');
  278. $args = array(
  279. 'body' => array(
  280. 'backup_cron_action' => 'mmb_remote_upload',
  281. 'args' => json_encode(array('task_name' => $task_name, 'backup_file' => $backup_file, 'del_host_file' => $del_host_file)),
  282. 'mmb_backup_nonce' => $nonce,
  283. ),
  284. 'timeout' => 0.01,
  285. 'blocking' => false,
  286. 'sslverify' => apply_filters('https_local_ssl_verify', true)
  287. );
  288. wp_remote_post($cron_url, $args);*/
  289. }
  290. }
  291. break; //Only one backup per cron
  292. }
  293. }
  294. }
  295. }
  296. /**
  297. * Runs backup task invoked from ManageWP master.
  298. *
  299. * @param string $task_name name of backup task
  300. * @param string|bool[optional] $google_drive_token false if backup destination is not Google Drive, json of Google Drive token if it is remote destination (default: false)
  301. * @return mixed array with backup statistics if successful, array with error message if not
  302. */
  303. function task_now($task_name, $google_drive_token = false) {
  304. if ($google_drive_token) {
  305. $this->tasks[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $google_drive_token;
  306. }
  307. $settings = $this->tasks;
  308. if(!array_key_exists($task_name,$settings)){
  309. return array('error' => $task_name." does not exist.");
  310. } else {
  311. $setting = $settings[$task_name];
  312. }
  313. $this->set_backup_task(array(
  314. 'task_name' => $task_name,
  315. 'args' => $settings[$task_name]['task_args'],
  316. 'time' => time()
  317. ));
  318. //Run backup
  319. $result = $this->backup($setting['task_args'], $task_name);
  320. //Check for error
  321. if (is_array($result) && array_key_exists('error', $result)) {
  322. $this->set_backup_task(array(
  323. 'task_name' => $task_name,
  324. 'args' => $settings[$task_name]['task_args'],
  325. 'error' => $result
  326. ));
  327. return $result;
  328. } else {
  329. return $this->get_backup_stats();
  330. }
  331. }
  332. /**
  333. * Backup a full wordpress instance, including a database dump, which is placed in mwp_db dir in root folder.
  334. * All backups are compressed by zip and placed in wp-content/managewp/backups folder.
  335. *
  336. * @param string $args arguments passed from master
  337. * [type] -> db, full,
  338. * [what] -> daily, weekly, monthly,
  339. * [account_info] -> remote destinations ftp, amazons3, dropbox, google_drive, email with their parameters
  340. * [include] -> array of folders from site root which are included to backup (wp-admin, wp-content, wp-includes are default)
  341. * [exclude] -> array of files of folders to exclude, relative to site's root
  342. * @param bool|string[optional] $task_name the name of backup task, which backup is done (default: false)
  343. * @return bool|array false if $args are missing, array with error if error has occured, ture if is successful
  344. */
  345. function backup($args, $task_name = false) {
  346. if (!$args || empty($args))
  347. return false;
  348. extract($args); //extract settings
  349. if (!empty($account_info)) {
  350. $found = false;
  351. $destinations = array('mwp_ftp', 'mwp_amazon_s3', 'mwp_dropbox', 'mwp_google_drive', 'mwp_email');
  352. foreach($destinations as $dest) {
  353. $found = $found || (isset($account_info[$dest]));
  354. }
  355. if (!$found) {
  356. $error_message = 'Remote destination is not supported, please update your client plugin.';
  357. return array(
  358. 'error' => $error_message
  359. );
  360. }
  361. }
  362. //Try increase memory limit and execution time
  363. $this->set_memory();
  364. //Remove old backup(s)
  365. $removed = $this->remove_old_backups($task_name);
  366. if (is_array($removed) && isset($removed['error'])) {
  367. $error_message = $removed['error'];
  368. return $removed;
  369. }
  370. $new_file_path = MWP_BACKUP_DIR;
  371. if (!file_exists($new_file_path)) {
  372. if (!mkdir($new_file_path, 0755, true))
  373. $error_message = 'Permission denied, make sure you have write permission to wp-content folder.';
  374. return array(
  375. 'error' => $error_message
  376. );
  377. }
  378. @file_put_contents($new_file_path . '/index.php', ''); //safe
  379. //Prepare .zip file name
  380. $hash = md5(time());
  381. $label = $type ? $type : 'manual';
  382. $backup_file = $new_file_path . '/' . $this->site_name . '_' . $label . '_' . $what . '_' . date('Y-m-d') . '_' . $hash . '.zip';
  383. $backup_url = WP_CONTENT_URL . '/managewp/backups/' . $this->site_name . '_' . $label . '_' . $what . '_' . date('Y-m-d') . '_' . $hash . '.zip';
  384. $begin_compress = microtime(true);
  385. //Optimize tables?
  386. if (isset($optimize_tables) && !empty($optimize_tables)) {
  387. $this->optimize_tables();
  388. }
  389. //What to backup - db or full?
  390. if (trim($what) == 'db') {
  391. $db_backup = $this->backup_db_compress($task_name, $backup_file);
  392. if (is_array($db_backup) && array_key_exists('error', $db_backup)) {
  393. $error_message = $db_backup['error'];
  394. return array(
  395. 'error' => $error_message
  396. );
  397. }
  398. } elseif (trim($what) == 'full') {
  399. if (!$exclude) {
  400. $exclude = array();
  401. }
  402. if (!$include) {
  403. $include = array();
  404. }
  405. $content_backup = $this->backup_full($task_name, $backup_file, $exclude, $include);
  406. if (is_array($content_backup) && array_key_exists('error', $content_backup)) {
  407. $error_message = $content_backup['error'];
  408. return array(
  409. 'error' => $error_message
  410. );
  411. }
  412. }
  413. $end_compress = microtime(true);
  414. //Update backup info
  415. if ($task_name) {
  416. //backup task (scheduled)
  417. $backup_settings = $this->tasks;
  418. $paths = array();
  419. $size = ceil(filesize($backup_file) / 1024);
  420. $duration = round($end_compress - $begin_compress, 2);
  421. if ($size > 1000) {
  422. $paths['size'] = ceil($size / 1024) . "mb";
  423. } else {
  424. $paths['size'] = $size . 'kb';
  425. }
  426. $paths['duration'] = $duration . 's';
  427. if ($task_name != 'Backup Now') {
  428. $paths['server'] = array(
  429. 'file_path' => $backup_file,
  430. 'file_url' => $backup_url
  431. );
  432. } else {
  433. $paths['server'] = array(
  434. 'file_path' => $backup_file,
  435. 'file_url' => $backup_url
  436. );
  437. }
  438. if (isset($backup_settings[$task_name]['task_args']['account_info']['mwp_ftp'])) {
  439. $paths['ftp'] = basename($backup_url);
  440. }
  441. if (isset($backup_settings[$task_name]['task_args']['account_info']['mwp_amazon_s3'])) {
  442. $paths['amazons3'] = basename($backup_url);
  443. }
  444. if (isset($backup_settings[$task_name]['task_args']['account_info']['mwp_dropbox'])) {
  445. $paths['dropbox'] = basename($backup_url);
  446. }
  447. if (isset($backup_settings[$task_name]['task_args']['account_info']['mwp_email'])) {
  448. $paths['email'] = basename($backup_url);
  449. }
  450. if (isset($backup_settings[$task_name]['task_args']['account_info']['mwp_google_drive'])) {
  451. $paths['google_drive'] = basename($backup_url);
  452. }
  453. $temp = $backup_settings[$task_name]['task_results'];
  454. $temp = array_values($temp);
  455. $paths['time'] = time();
  456. if ($task_name != 'Backup Now') {
  457. $paths['status'] = $temp[count($temp) - 1]['status'];
  458. $temp[count($temp) - 1] = $paths;
  459. } else {
  460. $temp[count($temp)] = $paths;
  461. }
  462. $backup_settings[$task_name]['task_results'] = $temp;
  463. $this->update_tasks($backup_settings);
  464. //update_option('mwp_backup_tasks', $backup_settings);
  465. }
  466. // If there are not remote destination, set up task status to finished
  467. if (@count($backup_settings[$task_name]['task_args']['account_info']) == 0) {
  468. $this->update_status($task_name, $this->statuses['finished'], true);
  469. }
  470. return true;
  471. }
  472. /**
  473. * Backup a full wordpress instance, including a database dump, which is placed in mwp_db dir in root folder.
  474. * All backups are compressed by zip and placed in wp-content/managewp/backups folder.
  475. *
  476. * @param string $task_name the name of backup task, which backup is done
  477. * @param string $backup_file relative path to file which backup is stored
  478. * @param array[optional] $exclude the list of files and folders, which are excluded from backup (default: array())
  479. * @param array[optional] $include the list of folders in wordpress root which are included to backup, expect wp-admin, wp-content, wp-includes, which are default (default: array())
  480. * @return bool|array true if backup is successful, or an array with error message if is failed
  481. */
  482. function backup_full($task_name, $backup_file, $exclude = array(), $include = array()) {
  483. $this->update_status($task_name, $this->statuses['db_dump']);
  484. $db_result = $this->backup_db();
  485. if ($db_result == false) {
  486. return array(
  487. 'error' => 'Failed to backup database.'
  488. );
  489. } else if (is_array($db_result) && isset($db_result['error'])) {
  490. return array(
  491. 'error' => $db_result['error']
  492. );
  493. }
  494. $this->update_status($task_name, $this->statuses['db_dump'], true);
  495. $this->update_status($task_name, $this->statuses['db_zip']);
  496. @file_put_contents(MWP_BACKUP_DIR.'/mwp_db/index.php', '');
  497. $zip_db_result = $this->zip_backup_db($task_name, $backup_file);
  498. if (!$zip_db_result) {
  499. $zip_archive_db_result = false;
  500. if (class_exists("ZipArchive")) {
  501. $this->_log("DB zip, fallback to ZipArchive");
  502. $zip_archive_db_result = $this->zip_archive_backup_db($task_name, $db_result, $backup_file);
  503. }
  504. if (!$zip_archive_db_result) {
  505. $this->_log("DB zip, fallback to PclZip");
  506. $pclzip_db_result = $this->pclzip_backup_db($task_name, $backup_file);
  507. if (!$pclzip_db_result) {
  508. @unlink(MWP_BACKUP_DIR.'/mwp_db/index.php');
  509. @unlink($db_result);
  510. @rmdir(MWP_DB_DIR);
  511. return array(
  512. 'error' => 'Failed to zip database. pclZip error (' . $archive->error_code . '): .' . $archive->error_string
  513. );
  514. }
  515. }
  516. }
  517. @unlink(MWP_BACKUP_DIR.'/mwp_db/index.php');
  518. @unlink($db_result);
  519. @rmdir(MWP_DB_DIR);
  520. $remove = array(
  521. trim(basename(WP_CONTENT_DIR)) . "/managewp/backups",
  522. trim(basename(WP_CONTENT_DIR)) . "/" . md5('mmb-worker') . "/mwp_backups"
  523. );
  524. $exclude = array_merge($exclude, $remove);
  525. $this->update_status($task_name, $this->statuses['db_zip'], true);
  526. $this->update_status($task_name, $this->statuses['files_zip']);
  527. $zip_result = $this->zip_backup($task_name, $backup_file, $exclude, $include);
  528. if (isset($zip_result['error'])) {
  529. return $zip_result;
  530. }
  531. if (!$zip_result) {
  532. $zip_archive_result = false;
  533. if (class_exists("ZipArchive")) {
  534. $this->_log("Files zip fallback to ZipArchive");
  535. $zip_archive_result = $this->zip_archive_backup($task_name, $backup_file, $exclude, $include);
  536. }
  537. if (!$zip_archive_result) {
  538. $this->_log("Files zip fallback to PclZip");
  539. $pclzip_result = $this->pclzip_backup($task_name, $backup_file, $exclude, $include);
  540. if (!$pclzip_result) {
  541. @unlink(MWP_BACKUP_DIR.'/mwp_db/index.php');
  542. @unlink($db_result);
  543. @rmdir(MWP_DB_DIR);
  544. if (!$pclzip_result) {
  545. @unlink($backup_file);
  546. return array(
  547. 'error' => 'Failed to zip files. pclZip error (' . $archive->error_code . '): .' . $archive->error_string
  548. );
  549. }
  550. }
  551. }
  552. }
  553. //Reconnect
  554. $this->wpdb_reconnect();
  555. $this->update_status($task_name, $this->statuses['files_zip'], true);
  556. return true;
  557. }
  558. /**
  559. * Zipping database dump and index.php in folder mwp_db by system zip command, requires zip installed on OS.
  560. *
  561. * @param string $task_name the name of backup task
  562. * @param string $backup_file absolute path to zip file
  563. * @return bool is compress successful or not
  564. */
  565. function zip_backup_db($task_name, $backup_file) {
  566. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  567. $comp_level = $disable_comp ? '-0' : '-1';
  568. $zip = $this->get_zip();
  569. //Add database file
  570. chdir(MWP_BACKUP_DIR);
  571. $command = "$zip -q -r $comp_level $backup_file 'mwp_db'";
  572. ob_start();
  573. $this->_log("Executing $command");
  574. $result = $this->mmb_exec($command);
  575. ob_get_clean();
  576. return $result;
  577. }
  578. /**
  579. * Zipping database dump and index.php in folder mwp_db by ZipArchive class, requires php zip extension.
  580. *
  581. * @param string $task_name the name of backup task
  582. * @param string $db_result relative path to database dump file
  583. * @param string $backup_file absolute path to zip file
  584. * @return bool is compress successful or not
  585. */
  586. function zip_archive_backup_db($task_name, $db_result, $backup_file) {
  587. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  588. if (!$disable_comp) {
  589. $this->_log("Compression is not supported by ZipArchive");
  590. }
  591. $zip = new ZipArchive();
  592. $result = $zip->open($backup_file, ZIPARCHIVE::OVERWRITE); // Tries to open $backup_file for acrhiving
  593. if ($result === true) {
  594. $result = $result && $zip->addFile(MWP_BACKUP_DIR.'/mwp_db/index.php', "mwp_db/index.php"); // Tries to add mwp_db/index.php to $backup_file
  595. $result = $result && $zip->addFile($db_result, "mwp_db/" . basename($db_result)); // Tries to add db dump form mwp_db dir to $backup_file
  596. $result = $result && $zip->close(); // Tries to close $backup_file
  597. } else {
  598. $result = false;
  599. }
  600. return $result; // true if $backup_file iz zipped successfully, false if error is occured in zip process
  601. }
  602. /**
  603. * Zipping database dump and index.php in folder mwp_db by PclZip library.
  604. *
  605. * @param string $task_name the name of backup task
  606. * @param string $backup_file absolute path to zip file
  607. * @return bool is compress successful or not
  608. */
  609. function pclzip_backup_db($task_name, $backup_file) {
  610. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  611. define('PCLZIP_TEMPORARY_DIR', MWP_BACKUP_DIR . '/');
  612. require_once ABSPATH . '/wp-admin/includes/class-pclzip.php';
  613. $zip = new PclZip($backup_file);
  614. if ($disable_comp) {
  615. $result = $zip->add(MWP_BACKUP_DIR, PCLZIP_OPT_REMOVE_PATH, MWP_BACKUP_DIR, PCLZIP_OPT_NO_COMPRESSION) !== 0;
  616. } else {
  617. $result = $zip->add(MWP_BACKUP_DIR, PCLZIP_OPT_REMOVE_PATH, MWP_BACKUP_DIR) !== 0;
  618. }
  619. return $result;
  620. }
  621. /**
  622. * Zipping whole site root folder and append to backup file with database dump
  623. * by system zip command, requires zip installed on OS.
  624. *
  625. * @param string $task_name the name of backup task
  626. * @param string $backup_file absolute path to zip file
  627. * @param array $exclude array of files of folders to exclude, relative to site's root
  628. * @param array $include array of folders from site root which are included to backup (wp-admin, wp-content, wp-includes are default)
  629. * @return array|bool true if successful or an array with error message if not
  630. */
  631. function zip_backup($task_name, $backup_file, $exclude, $include) {
  632. global $zip_errors;
  633. $sys = substr(PHP_OS, 0, 3);
  634. //Exclude paths
  635. $exclude_data = "-x";
  636. $exclude_file_data = '';
  637. if (!empty($exclude)) {
  638. foreach ($exclude as $data) {
  639. if (is_dir(ABSPATH . $data)) {
  640. if ($sys == 'WIN')
  641. $exclude_data .= " $data/*.*";
  642. else
  643. $exclude_data .= " '$data/*'";
  644. } else {
  645. if ($sys == 'WIN'){
  646. if(file_exists(ABSPATH . $data)){
  647. $exclude_data .= " $data";
  648. $exclude_file_data .= " $data";
  649. }
  650. } else {
  651. if(file_exists(ABSPATH . $data)){
  652. $exclude_data .= " '$data'";
  653. $exclude_file_data .= " '$data'";
  654. }
  655. }
  656. }
  657. }
  658. }
  659. if($exclude_file_data){
  660. $exclude_file_data = "-x".$exclude_file_data;
  661. }
  662. //Include paths by default
  663. $add = array(
  664. trim(WPINC),
  665. trim(basename(WP_CONTENT_DIR)),
  666. "wp-admin"
  667. );
  668. $include_data = ". -i";
  669. foreach ($add as $data) {
  670. if ($sys == 'WIN')
  671. $include_data .= " $data/*.*";
  672. else
  673. $include_data .= " '$data/*'";
  674. }
  675. //Additional includes?
  676. if (!empty($include)) {
  677. foreach ($include as $data) {
  678. if ($data) {
  679. if ($sys == 'WIN')
  680. $include_data .= " $data/*.*";
  681. else
  682. $include_data .= " '$data/*'";
  683. }
  684. }
  685. }
  686. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  687. $comp_level = $disable_comp ? '-0' : '-1';
  688. $zip = $this->get_zip();
  689. chdir(ABSPATH);
  690. ob_start();
  691. $command = "$zip -q -j $comp_level $backup_file .* * $exclude_file_data";
  692. $this->_log("Executing $command");
  693. $result_f = $this->mmb_exec($command, false, true);
  694. if (!$result_f || $result_f == 18) { // disregard permissions error, file can't be accessed
  695. $command = "$zip -q -r $comp_level $backup_file $include_data $exclude_data";
  696. $result_d = $this->mmb_exec($command, false, true);
  697. $this->_log("Executing $command");
  698. if ($result_d && $result_d != 18) {
  699. @unlink($backup_file);
  700. if ($result_d > 0 && $result_d < 18)
  701. return array(
  702. 'error' => 'Failed to archive files (' . $zip_errors[$result_d] . ') .'
  703. );
  704. else {
  705. if ($result_d === -1) return false;
  706. return array(
  707. 'error' => 'Failed to archive files.'
  708. );
  709. }
  710. }
  711. } else {
  712. return false;
  713. }
  714. ob_get_clean();
  715. return true;
  716. }
  717. /**
  718. * Zipping whole site root folder and append to backup file with database dump
  719. * by ZipArchive class, requires php zip extension.
  720. *
  721. * @param string $task_name the name of backup task
  722. * @param string $backup_file absolute path to zip file
  723. * @param array $exclude array of files of folders to exclude, relative to site's root
  724. * @param array $include array of folders from site root which are included to backup (wp-admin, wp-content, wp-includes are default)
  725. * @return array|bool true if successful or an array with error message if not
  726. */
  727. function zip_archive_backup($task_name, $backup_file, $exclude, $include, $overwrite = false) {
  728. $filelist = $this->get_backup_files($exclude, $include);
  729. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  730. if (!$disable_comp) {
  731. $this->_log("Compression is not supported by ZipArchive");
  732. }
  733. $zip = new ZipArchive();
  734. if ($overwrite) {
  735. $result = $zip->open($backup_file, ZipArchive::OVERWRITE); // Tries to open $backup_file for acrhiving
  736. } else {
  737. $result = $zip->open($backup_file); // Tries to open $backup_file for acrhiving
  738. }
  739. if ($result === true) {
  740. foreach ($filelist as $file) {
  741. $result = $result && $zip->addFile($file, sprintf("%s", str_replace(ABSPATH, '', $file))); // Tries to add a new file to $backup_file
  742. }
  743. $result = $result && $zip->close(); // Tries to close $backup_file
  744. } else {
  745. $result = false;
  746. }
  747. return $result; // true if $backup_file iz zipped successfully, false if error is occured in zip process
  748. }
  749. /**
  750. * Zipping whole site root folder and append to backup file with database dump
  751. * by PclZip library.
  752. *
  753. * @param string $task_name the name of backup task
  754. * @param string $backup_file absolute path to zip file
  755. * @param array $exclude array of files of folders to exclude, relative to site's root
  756. * @param array $include array of folders from site root which are included to backup (wp-admin, wp-content, wp-includes are default)
  757. * @return array|bool true if successful or an array with error message if not
  758. */
  759. function pclzip_backup($task_name, $backup_file, $exclude, $include) {
  760. define('PCLZIP_TEMPORARY_DIR', MWP_BACKUP_DIR . '/');
  761. require_once ABSPATH . '/wp-admin/includes/class-pclzip.php';
  762. $zip = new PclZip($backup_file);
  763. $add = array(
  764. trim(WPINC),
  765. trim(basename(WP_CONTENT_DIR)),
  766. "wp-admin"
  767. );
  768. $include_data = array();
  769. if (!empty($include)) {
  770. foreach ($include as $data) {
  771. if ($data && file_exists(ABSPATH . $data))
  772. $include_data[] = ABSPATH . $data . '/';
  773. }
  774. }
  775. $include_data = array_merge($add, $include_data);
  776. if ($handle = opendir(ABSPATH)) {
  777. while (false !== ($file = readdir($handle))) {
  778. if ($file != "." && $file != ".." && !is_dir($file) && file_exists(ABSPATH . $file)) {
  779. $include_data[] = ABSPATH . $file;
  780. }
  781. }
  782. closedir($handle);
  783. }
  784. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  785. if ($disable_comp) {
  786. $result = $zip->add($include_data, PCLZIP_OPT_REMOVE_PATH, ABSPATH, PCLZIP_OPT_NO_COMPRESSION) !== 0;
  787. } else {
  788. $result = $zip->add($include_data, PCLZIP_OPT_REMOVE_PATH, ABSPATH) !== 0;
  789. }
  790. $exclude_data = array();
  791. if (!empty($exclude)) {
  792. foreach ($exclude as $data) {
  793. if (file_exists(ABSPATH . $data)) {
  794. if (is_dir(ABSPATH . $data))
  795. $exclude_data[] = $data . '/';
  796. else
  797. $exclude_data[] = $data;
  798. }
  799. }
  800. }
  801. $result = $result && $zip->delete(PCLZIP_OPT_BY_NAME, $exclude_data);
  802. return $result;
  803. }
  804. /**
  805. * Gets an array of relative paths of all files in site root recursively.
  806. * By default, there are all files from root folder, all files from folders wp-admin, wp-content, wp-includes recursively.
  807. * Parameter $include adds other folders from site root, and excludes any file or folder by relative path to site's root.
  808. *
  809. * @param array $exclude array of files of folders to exclude, relative to site's root
  810. * @param array $include array of folders from site root which are included to backup (wp-admin, wp-content, wp-includes are default)
  811. * @return array array with all files in site root dir
  812. */
  813. function get_backup_files($exclude, $include) {
  814. $add = array(
  815. trim(WPINC),
  816. trim(basename(WP_CONTENT_DIR)),
  817. "wp-admin"
  818. );
  819. $include = array_merge($add, $include);
  820. $filelist = array();
  821. if ($handle = opendir(ABSPATH)) {
  822. while (false !== ($file = readdir($handle))) {
  823. if (is_dir($file) && file_exists(ABSPATH . $file) && !(in_array($file, $include))) {
  824. $exclude[] = $file;
  825. }
  826. }
  827. closedir($handle);
  828. }
  829. $filelist = get_all_files_from_dir(ABSPATH, $exclude);
  830. return $filelist;
  831. }
  832. /**
  833. * Backup a database dump of WordPress site.
  834. * All backups are compressed by zip and placed in wp-content/managewp/backups folder.
  835. *
  836. * @param string $task_name the name of backup task, which backup is done
  837. * @param string $backup_file relative path to file which backup is stored
  838. * @return bool|array true if backup is successful, or an array with error message if is failed
  839. */
  840. function backup_db_compress($task_name, $backup_file) {
  841. $this->update_status($task_name, $this->statuses['db_dump']);
  842. $db_result = $this->backup_db();
  843. if ($db_result == false) {
  844. return array(
  845. 'error' => 'Failed to backup database.'
  846. );
  847. } else if (is_array($db_result) && isset($db_result['error'])) {
  848. return array(
  849. 'error' => $db_result['error']
  850. );
  851. }
  852. $this->update_status($task_name, $this->statuses['db_dump'], true);
  853. $this->update_status($task_name, $this->statuses['db_zip']);
  854. @file_put_contents(MWP_BACKUP_DIR.'/mwp_db/index.php', '');
  855. $zip_db_result = $this->zip_backup_db($task_name, $backup_file);
  856. if (!$zip_db_result) {
  857. $zip_archive_db_result = false;
  858. if (class_exists("ZipArchive")) {
  859. $this->_log("DB zip, fallback to ZipArchive");
  860. $zip_archive_db_result = $this->zip_archive_backup_db($task_name, $db_result, $backup_file);
  861. }
  862. if (!$zip_archive_db_result) {
  863. $this->_log("DB zip, fallback to PclZip");
  864. $pclzip_db_result = $this->pclzip_backup_db($task_name, $backup_file);
  865. if (!$pclzip_db_result) {
  866. @unlink(MWP_BACKUP_DIR.'/mwp_db/index.php');
  867. @unlink($db_result);
  868. @rmdir(MWP_DB_DIR);
  869. return array(
  870. 'error' => 'Failed to zip database. pclZip error (' . $archive->error_code . '): .' . $archive->error_string
  871. );
  872. }
  873. }
  874. }
  875. @unlink(MWP_BACKUP_DIR.'/mwp_db/index.php');
  876. @unlink($db_result);
  877. @rmdir(MWP_DB_DIR);
  878. $this->update_status($task_name, $this->statuses['db_zip'], true);
  879. return true;
  880. }
  881. /**
  882. * Creates database dump and places it in mwp_db folder in site's root.
  883. * This function dispatches if OS mysql command does not work calls a php alternative.
  884. *
  885. * @return string|array path to dump file if successful, or an array with error message if is failed
  886. */
  887. function backup_db() {
  888. $db_folder = MWP_DB_DIR . '/';
  889. if (!file_exists($db_folder)) {
  890. if (!mkdir($db_folder, 0755, true))
  891. return array(
  892. 'error' => 'Error creating database backup folder (' . $db_folder . '). Make sure you have corrrect write permissions.'
  893. );
  894. }
  895. $file = $db_folder . DB_NAME . '.sql';
  896. $result = $this->backup_db_dump($file); // try mysqldump always then fallback to php dump
  897. return $result;
  898. }
  899. /**
  900. * Creates database dump by system mysql command.
  901. *
  902. * @param string $file absolute path to file in which dump should be placed
  903. * @return string|array path to dump file if successful, or an array with error message if is failed
  904. */
  905. function backup_db_dump($file) {
  906. global $wpdb;
  907. $paths = $this->check_mysql_paths();
  908. $brace = (substr(PHP_OS, 0, 3) == 'WIN') ? '"' : '';
  909. $command = $brace . $paths['mysqldump'] . $brace . ' --force --host="' . DB_HOST . '" --user="' . DB_USER . '" --password="' . DB_PASSWORD . '" --add-drop-table --skip-lock-tables "' . DB_NAME . '" > ' . $brace . $file . $brace;
  910. ob_start();
  911. $result = $this->mmb_exec($command);
  912. ob_get_clean();
  913. if (!$result) { // Fallback to php
  914. $this->_log("DB dump fallback to php");
  915. $result = $this->backup_db_php($file);
  916. return $result;
  917. }
  918. if (filesize($file) == 0 || !is_file($file) || !$result) {
  919. @unlink($file);
  920. return false;
  921. } else {
  922. return $file;
  923. }
  924. }
  925. /**
  926. * Creates database dump by php functions.
  927. *
  928. * @param string $file absolute path to file in which dump should be placed
  929. * @return string|array path to dump file if successful, or an array with error message if is failed
  930. */
  931. function backup_db_php($file) {
  932. global $wpdb;
  933. $tables = $wpdb->get_results('SHOW TABLES', ARRAY_N);
  934. foreach ($tables as $table) {
  935. //drop existing table
  936. $dump_data = "DROP TABLE IF EXISTS $table[0];";
  937. file_put_contents($file, $dump_data, FILE_APPEND);
  938. //create table
  939. $create_table = $wpdb->get_row("SHOW CREATE TABLE $table[0]", ARRAY_N);
  940. $dump_data = "\n\n" . $create_table[1] . ";\n\n";
  941. file_put_contents($file, $dump_data, FILE_APPEND);
  942. $count = $wpdb->get_var("SELECT count(*) FROM $table[0]");
  943. if ($count > 100)
  944. $count = ceil($count / 100);
  945. else if ($count > 0)
  946. $count = 1;
  947. for ($i = 0; $i < $count; $i++) {
  948. $low_limit = $i * 100;
  949. $qry = "SELECT * FROM $table[0] LIMIT $low_limit, 100";
  950. $rows = $wpdb->get_results($qry, ARRAY_A);
  951. if (is_array($rows)) {
  952. foreach ($rows as $row) {
  953. //insert single row
  954. $dump_data = "INSERT INTO $table[0] VALUES(";
  955. $num_values = count($row);
  956. $j = 1;
  957. foreach ($row as $value) {
  958. $value = addslashes($value);
  959. $value = preg_replace("/\n/Ui", "\\n", $value);
  960. $num_values == $j ? $dump_data .= "'" . $value . "'" : $dump_data .= "'" . $value . "', ";
  961. $j++;
  962. unset($value);
  963. }
  964. $dump_data .= ");\n";
  965. file_put_contents($file, $dump_data, FILE_APPEND);
  966. }
  967. }
  968. }
  969. $dump_data = "\n\n\n";
  970. file_put_contents($file, $dump_data, FILE_APPEND);
  971. unset($rows);
  972. unset($dump_data);
  973. }
  974. if (filesize($file) == 0 || !is_file($file)) {
  975. @unlink($file);
  976. return array(
  977. 'error' => 'Database backup failed. Try to enable MySQL dump on your server.'
  978. );
  979. }
  980. return $file;
  981. }
  982. /**
  983. * Restores full WordPress site or database only form backup zip file.
  984. *
  985. * @param array array of arguments passed to backup restore
  986. * [task_name] -> name of backup task
  987. * [result_id] -> id of baskup task result, which should be restored
  988. * [google_drive_token] -> json of Google Drive token, if it is remote destination
  989. * @return bool|array true if successful, or an array with error message if is failed
  990. */
  991. function restore($args) {
  992. global $wpdb;
  993. if (empty($args)) {
  994. return false;
  995. }
  996. extract($args);
  997. if (isset($google_drive_token)) {
  998. $this->tasks[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $google_drive_token;
  999. }
  1000. $this->set_memory();
  1001. $unlink_file = true; //Delete file after restore
  1002. //Detect source
  1003. if ($backup_url) {
  1004. //This is for clone (overwrite)
  1005. include_once ABSPATH . 'wp-admin/includes/file.php';
  1006. $backup_file = download_url($backup_url);
  1007. if (is_wp_error($backup_file)) {
  1008. return array(
  1009. 'error' => 'Unable to download backup file ('.$backup_file->get_error_message().')'
  1010. );
  1011. }
  1012. $what = 'full';
  1013. } else {
  1014. $tasks = $this->tasks;
  1015. $task = $tasks[$task_name];
  1016. if (isset($task['task_results'][$result_id]['server'])) {
  1017. $backup_file = $task['task_results'][$result_id]['server']['file_path'];
  1018. $unlink_file = false; //Don't delete file if stored on server
  1019. } elseif (isset($task['task_results'][$result_id]['ftp'])) {
  1020. $ftp_file = $task['task_results'][$result_id]['ftp'];
  1021. $args = $task['task_args']['account_info']['mwp_ftp'];
  1022. $args['backup_file'] = $ftp_file;
  1023. $backup_file = $this->get_ftp_backup($args);
  1024. if ($backup_file == false) {
  1025. return array(
  1026. 'error' => 'Failed to download file from FTP.'
  1027. );
  1028. }
  1029. } elseif (isset($task['task_results'][$result_id]['amazons3'])) {
  1030. $amazons3_file = $task['task_results'][$result_id]['amazons3'];
  1031. $args = $task['task_args']['account_info']['mwp_amazon_s3'];
  1032. $args['backup_file'] = $amazons3_file;
  1033. $backup_file = $this->get_amazons3_backup($args);
  1034. if ($backup_file == false) {
  1035. return array(
  1036. 'error' => 'Failed to download file from Amazon S3.'
  1037. );
  1038. }
  1039. } elseif(isset($task['task_results'][$result_id]['dropbox'])){
  1040. $dropbox_file = $task['task_results'][$result_id]['dropbox'];
  1041. $args = $task['task_args']['account_info']['mwp_dropbox'];
  1042. $args['backup_file'] = $dropbox_file;
  1043. $backup_file = $this->get_dropbox_backup($args);
  1044. if ($backup_file == false) {
  1045. return array(
  1046. 'error' => 'Failed to download file from Dropbox.'
  1047. );
  1048. }
  1049. } elseif (isset($task['task_results'][$result_id]['google_drive'])) {
  1050. $google_drive_file = $task['task_results'][$result_id]['google_drive'];
  1051. $args = $task['task_args']['account_info']['mwp_google_drive'];
  1052. $args['backup_file'] = $google_drive_file;
  1053. $backup_file = $this->get_google_drive_backup($args);
  1054. if (is_array($backup_file) && isset($backup_file['error'])) {
  1055. return array(
  1056. 'error' => 'Failed to download file from Google Drive, reason: ' . $backup_file['error']
  1057. );
  1058. } elseif ($backup_file == false) {
  1059. return array(
  1060. 'error' => 'Failed to download file from Google Drive.'
  1061. );
  1062. }
  1063. }
  1064. $what = $tasks[$task_name]['task_args']['what'];
  1065. }
  1066. $this->wpdb_reconnect();
  1067. if ($backup_file && file_exists($backup_file)) {
  1068. if ($overwrite) {
  1069. //Keep old db credentials before overwrite
  1070. if (!copy(ABSPATH . 'wp-config.php', ABSPATH . 'mwp-temp-wp-config.php')) {
  1071. @unlink($backup_file);
  1072. return array(
  1073. 'error' => 'Error creating wp-config. Please check your write permissions.'
  1074. );
  1075. }
  1076. $db_host = DB_HOST;
  1077. $db_user = DB_USER;
  1078. $db_password = DB_PASSWORD;
  1079. $home = rtrim(get_option('home'), "/");
  1080. $site_url = get_option('site_url');
  1081. $clone_options = array();
  1082. if (trim($clone_from_url) || trim($mwp_clone)) {
  1083. $clone_options['_worker_nossl_key'] = get_option('_worker_nossl_key');
  1084. $clone_options['_worker_public_key'] = get_option('_worker_public_key');
  1085. $clone_options['_action_message_id'] = get_option('_action_message_id');
  1086. }
  1087. $clone_options['upload_path'] = get_option('upload_path');
  1088. $clone_options['upload_url_path'] = get_option('upload_url_path');
  1089. $clone_options['mwp_backup_tasks'] = serialize(get_option('mwp_backup_tasks'));
  1090. $clone

Large files files are truncated, but you can click here to view the full file