PageRenderTime 68ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

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

https://bitbucket.org/summitds/bloomsburgpa.org
PHP | 3266 lines | 2238 code | 365 blank | 663 comment | 521 complexity | 8d91a885f7ae53a06e459e4bfa032a93 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, BSD-3-Clause, GPL-3.0, LGPL-2.1

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 (isset($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. if($check == 'paused' || $check == 'deleted'){
  229. continue;
  230. }
  231. $worker_upto_3_9_22 = (MMB_WORKER_VERSION <= '3.9.22'); // worker version is less or equals to 3.9.22
  232. // This is the patch done in worker 3.9.22 because old worked provided message in the following format:
  233. // token - not found or token - {...json...}
  234. // The new message is a serialized string with google_drive_token or message.
  235. if ($worker_upto_3_9_22) {
  236. $potential_token = substr($check, 8);
  237. if (substr($check, 0, 8) == 'token - ' && $potential_token != 'not found') {
  238. $this->tasks[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  239. $settings[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  240. $setting['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  241. }
  242. } else {
  243. $potential_token = isset($check['google_drive_token']) ? $check['google_drive_token'] : false;
  244. if ($potential_token) {
  245. $this->tasks[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  246. $settings[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  247. $setting['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $potential_token;
  248. }
  249. }
  250. }
  251. $update = array(
  252. 'task_name' => $task_name,
  253. 'args' => $settings[$task_name]['task_args']
  254. );
  255. if ($check != 'paused') {
  256. $update['time'] = time();
  257. }
  258. //Update task with next schedule
  259. $this->set_backup_task($update);
  260. $result = $this->backup($setting['task_args'], $task_name);
  261. $error = '';
  262. if (is_array($result) && array_key_exists('error', $result)) {
  263. $error = $result;
  264. $this->set_backup_task(array(
  265. 'task_name' => $task_name,
  266. 'args' => $settings[$task_name]['task_args'],
  267. 'error' => $error
  268. ));
  269. } else {
  270. if (@count($setting['task_args']['account_info'])) {
  271. // Old way through sheduling.
  272. // wp_schedule_single_event(time(), 'mmb_scheduled_remote_upload', array('args' => array('task_name' => $task_name)));
  273. $nonce = substr(wp_hash(wp_nonce_tick() . 'mmb-backup-nonce' . 0, 'nonce'), -12, 10);
  274. $cron_url = site_url('index.php');
  275. $backup_file = $this->tasks[$task_name]['task_results'][count($this->tasks[$task_name]['task_results']) - 1]['server']['file_url'];
  276. $del_host_file = $this->tasks[$task_name]['task_args']['del_host_file'];
  277. $public_key = get_option('_worker_public_key');
  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. 'public_key' => $public_key,
  284. ),
  285. 'timeout' => 0.01,
  286. 'blocking' => false,
  287. 'sslverify' => apply_filters('https_local_ssl_verify', true)
  288. );
  289. wp_remote_post($cron_url, $args);
  290. }
  291. }
  292. break; //Only one backup per cron
  293. }
  294. }
  295. }
  296. }
  297. /**
  298. * Runs backup task invoked from ManageWP master.
  299. *
  300. * @param string $task_name name of backup task
  301. * @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)
  302. * @return mixed array with backup statistics if successful, array with error message if not
  303. */
  304. function task_now($task_name, $google_drive_token = false) {
  305. if ($google_drive_token) {
  306. $this->tasks[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $google_drive_token;
  307. }
  308. $settings = $this->tasks;
  309. if(!array_key_exists($task_name,$settings)){
  310. return array('error' => $task_name." does not exist.");
  311. } else {
  312. $setting = $settings[$task_name];
  313. }
  314. $this->set_backup_task(array(
  315. 'task_name' => $task_name,
  316. 'args' => $settings[$task_name]['task_args'],
  317. 'time' => time()
  318. ));
  319. //Run backup
  320. $result = $this->backup($setting['task_args'], $task_name);
  321. //Check for error
  322. if (is_array($result) && array_key_exists('error', $result)) {
  323. $this->set_backup_task(array(
  324. 'task_name' => $task_name,
  325. 'args' => $settings[$task_name]['task_args'],
  326. 'error' => $result
  327. ));
  328. return $result;
  329. } else {
  330. return $this->get_backup_stats();
  331. }
  332. }
  333. /**
  334. * Backup a full wordpress instance, including a database dump, which is placed in mwp_db dir in root folder.
  335. * All backups are compressed by zip and placed in wp-content/managewp/backups folder.
  336. *
  337. * @param string $args arguments passed from master
  338. * [type] -> db, full
  339. * [what] -> daily, weekly, monthly
  340. * [account_info] -> remote destinations ftp, amazons3, dropbox, google_drive, email with their parameters
  341. * [include] -> array of folders from site root which are included to backup (wp-admin, wp-content, wp-includes are default)
  342. * [exclude] -> array of files of folders to exclude, relative to site's root
  343. * @param bool|string[optional] $task_name the name of backup task, which backup is done (default: false)
  344. * @return bool|array false if $args are missing, array with error if error has occured, ture if is successful
  345. */
  346. function backup($args, $task_name = false) {
  347. if (!$args || empty($args))
  348. return false;
  349. extract($args); //extract settings
  350. if (!empty($account_info)) {
  351. $found = false;
  352. $destinations = array('mwp_ftp', 'mwp_amazon_s3', 'mwp_dropbox', 'mwp_google_drive', 'mwp_email');
  353. foreach($destinations as $dest) {
  354. $found = $found || (isset($account_info[$dest]));
  355. }
  356. if (!$found) {
  357. $error_message = 'Remote destination is not supported, please update your client plugin.';
  358. return array(
  359. 'error' => $error_message
  360. );
  361. }
  362. }
  363. //Try increase memory limit and execution time
  364. $this->set_memory();
  365. //Remove old backup(s)
  366. $removed = $this->remove_old_backups($task_name);
  367. if (is_array($removed) && isset($removed['error'])) {
  368. $error_message = $removed['error'];
  369. return $removed;
  370. }
  371. $new_file_path = MWP_BACKUP_DIR;
  372. if (!file_exists($new_file_path)) {
  373. if (!mkdir($new_file_path, 0755, true))
  374. $error_message = 'Permission denied, make sure you have write permission to wp-content folder.';
  375. return array(
  376. 'error' => $error_message
  377. );
  378. }
  379. @file_put_contents($new_file_path . '/index.php', ''); //safe
  380. //Prepare .zip file name
  381. $hash = md5(time());
  382. $label = $type ? $type : 'manual';
  383. $backup_file = $new_file_path . '/' . $this->site_name . '_' . $label . '_' . $what . '_' . date('Y-m-d') . '_' . $hash . '.zip';
  384. $backup_url = WP_CONTENT_URL . '/managewp/backups/' . $this->site_name . '_' . $label . '_' . $what . '_' . date('Y-m-d') . '_' . $hash . '.zip';
  385. $begin_compress = microtime(true);
  386. //Optimize tables?
  387. if (isset($optimize_tables) && !empty($optimize_tables)) {
  388. $this->optimize_tables();
  389. }
  390. //What to backup - db or full?
  391. if (trim($what) == 'db') {
  392. $db_backup = $this->backup_db_compress($task_name, $backup_file);
  393. if (is_array($db_backup) && array_key_exists('error', $db_backup)) {
  394. $error_message = $db_backup['error'];
  395. return array(
  396. 'error' => $error_message
  397. );
  398. }
  399. } elseif (trim($what) == 'full') {
  400. if (!$exclude) {
  401. $exclude = array();
  402. }
  403. if (!$include) {
  404. $include = array();
  405. }
  406. $content_backup = $this->backup_full($task_name, $backup_file, $exclude, $include);
  407. if (is_array($content_backup) && array_key_exists('error', $content_backup)) {
  408. $error_message = $content_backup['error'];
  409. return array(
  410. 'error' => $error_message
  411. );
  412. }
  413. }
  414. $end_compress = microtime(true);
  415. //Update backup info
  416. if ($task_name) {
  417. //backup task (scheduled)
  418. $backup_settings = $this->tasks;
  419. $paths = array();
  420. $size = ceil(filesize($backup_file) / 1024);
  421. $duration = round($end_compress - $begin_compress, 2);
  422. if ($size > 1000) {
  423. $paths['size'] = ceil($size / 1024) . "mb";
  424. } else {
  425. $paths['size'] = $size . 'kb';
  426. }
  427. $paths['duration'] = $duration . 's';
  428. if ($task_name != 'Backup Now') {
  429. $paths['server'] = array(
  430. 'file_path' => $backup_file,
  431. 'file_url' => $backup_url
  432. );
  433. } else {
  434. $paths['server'] = array(
  435. 'file_path' => $backup_file,
  436. 'file_url' => $backup_url
  437. );
  438. }
  439. if (isset($backup_settings[$task_name]['task_args']['account_info']['mwp_ftp'])) {
  440. $paths['ftp'] = basename($backup_url);
  441. }
  442. if (isset($backup_settings[$task_name]['task_args']['account_info']['mwp_amazon_s3'])) {
  443. $paths['amazons3'] = basename($backup_url);
  444. }
  445. if (isset($backup_settings[$task_name]['task_args']['account_info']['mwp_dropbox'])) {
  446. $paths['dropbox'] = basename($backup_url);
  447. }
  448. if (isset($backup_settings[$task_name]['task_args']['account_info']['mwp_email'])) {
  449. $paths['email'] = basename($backup_url);
  450. }
  451. if (isset($backup_settings[$task_name]['task_args']['account_info']['mwp_google_drive'])) {
  452. $paths['google_drive'] = basename($backup_url);
  453. }
  454. $temp = $backup_settings[$task_name]['task_results'];
  455. $temp = array_values($temp);
  456. $paths['time'] = time();
  457. if ($task_name != 'Backup Now') {
  458. $paths['status'] = $temp[count($temp) - 1]['status'];
  459. $temp[count($temp) - 1] = $paths;
  460. } else {
  461. $temp[count($temp)] = $paths;
  462. }
  463. $backup_settings[$task_name]['task_results'] = $temp;
  464. $this->update_tasks($backup_settings);
  465. //update_option('mwp_backup_tasks', $backup_settings);
  466. }
  467. // If there are not remote destination, set up task status to finished
  468. if (@count($backup_settings[$task_name]['task_args']['account_info']) == 0) {
  469. $this->update_status($task_name, $this->statuses['finished'], true);
  470. }
  471. return true;
  472. }
  473. /**
  474. * Backup a full wordpress instance, including a database dump, which is placed in mwp_db dir in root folder.
  475. * All backups are compressed by zip and placed in wp-content/managewp/backups folder.
  476. *
  477. * @param string $task_name the name of backup task, which backup is done
  478. * @param string $backup_file relative path to file which backup is stored
  479. * @param array[optional] $exclude the list of files and folders, which are excluded from backup (default: array())
  480. * @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())
  481. * @return bool|array true if backup is successful, or an array with error message if is failed
  482. */
  483. function backup_full($task_name, $backup_file, $exclude = array(), $include = array()) {
  484. $this->update_status($task_name, $this->statuses['db_dump']);
  485. $db_result = $this->backup_db();
  486. if ($db_result == false) {
  487. return array(
  488. 'error' => 'Failed to backup database.'
  489. );
  490. } else if (is_array($db_result) && isset($db_result['error'])) {
  491. return array(
  492. 'error' => $db_result['error']
  493. );
  494. }
  495. $this->update_status($task_name, $this->statuses['db_dump'], true);
  496. $this->update_status($task_name, $this->statuses['db_zip']);
  497. @file_put_contents(MWP_BACKUP_DIR.'/mwp_db/index.php', '');
  498. $zip_db_result = $this->zip_backup_db($task_name, $backup_file);
  499. if (!$zip_db_result) {
  500. $zip_archive_db_result = false;
  501. if (class_exists("ZipArchive")) {
  502. $this->_log("DB zip, fallback to ZipArchive");
  503. $zip_archive_db_result = $this->zip_archive_backup_db($task_name, $db_result, $backup_file);
  504. }
  505. if (!$zip_archive_db_result) {
  506. $this->_log("DB zip, fallback to PclZip");
  507. $pclzip_db_result = $this->pclzip_backup_db($task_name, $backup_file);
  508. if (!$pclzip_db_result) {
  509. @unlink(MWP_BACKUP_DIR.'/mwp_db/index.php');
  510. @unlink($db_result);
  511. @rmdir(MWP_DB_DIR);
  512. if($archive->error_code!=''){
  513. $archive->error_code = 'pclZip error ('.$archive->error_code . '): .';
  514. }
  515. return array(
  516. 'error' => 'Failed to zip database. ' . $archive->error_code . $archive->error_string
  517. );
  518. }
  519. }
  520. }
  521. @unlink(MWP_BACKUP_DIR.'/mwp_db/index.php');
  522. @unlink($db_result);
  523. @rmdir(MWP_DB_DIR);
  524. $remove = array(
  525. trim(basename(WP_CONTENT_DIR)) . "/managewp/backups",
  526. trim(basename(WP_CONTENT_DIR)) . "/" . md5('mmb-worker') . "/mwp_backups"
  527. );
  528. $exclude = array_merge($exclude, $remove);
  529. $this->update_status($task_name, $this->statuses['db_zip'], true);
  530. $this->update_status($task_name, $this->statuses['files_zip']);
  531. $zip_result = $this->zip_backup($task_name, $backup_file, $exclude, $include);
  532. if (isset($zip_result['error'])) {
  533. return $zip_result;
  534. }
  535. if (!$zip_result) {
  536. $zip_archive_result = false;
  537. if (class_exists("ZipArchive")) {
  538. $this->_log("Files zip fallback to ZipArchive");
  539. $zip_archive_result = $this->zip_archive_backup($task_name, $backup_file, $exclude, $include);
  540. }
  541. if (!$zip_archive_result) {
  542. $this->_log("Files zip fallback to PclZip");
  543. $pclzip_result = $this->pclzip_backup($task_name, $backup_file, $exclude, $include);
  544. if (!$pclzip_result) {
  545. @unlink(MWP_BACKUP_DIR.'/mwp_db/index.php');
  546. @unlink($db_result);
  547. @rmdir(MWP_DB_DIR);
  548. if (!$pclzip_result) {
  549. @unlink($backup_file);
  550. return array(
  551. 'error' => 'Failed to zip files. pclZip error (' . $archive->error_code . '): .' . $archive->error_string
  552. );
  553. }
  554. }
  555. }
  556. }
  557. //Reconnect
  558. $this->wpdb_reconnect();
  559. $this->update_status($task_name, $this->statuses['files_zip'], true);
  560. return true;
  561. }
  562. /**
  563. * Zipping database dump and index.php in folder mwp_db by system zip command, requires zip installed on OS.
  564. *
  565. * @param string $task_name the name of backup task
  566. * @param string $backup_file absolute path to zip file
  567. * @return bool is compress successful or not
  568. */
  569. function zip_backup_db($task_name, $backup_file) {
  570. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  571. $comp_level = $disable_comp ? '-0' : '-1';
  572. $zip = $this->get_zip();
  573. //Add database file
  574. chdir(MWP_BACKUP_DIR);
  575. $command = "$zip -q -r $comp_level $backup_file 'mwp_db'";
  576. ob_start();
  577. $this->_log("Executing $command");
  578. $result = $this->mmb_exec($command);
  579. ob_get_clean();
  580. return $result;
  581. }
  582. /**
  583. * Zipping database dump and index.php in folder mwp_db by ZipArchive class, requires php zip extension.
  584. *
  585. * @param string $task_name the name of backup task
  586. * @param string $db_result relative path to database dump file
  587. * @param string $backup_file absolute path to zip file
  588. * @return bool is compress successful or not
  589. */
  590. function zip_archive_backup_db($task_name, $db_result, $backup_file) {
  591. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  592. if (!$disable_comp) {
  593. $this->_log("Compression is not supported by ZipArchive");
  594. }
  595. $zip = new ZipArchive();
  596. $result = $zip->open($backup_file, ZIPARCHIVE::OVERWRITE); // Tries to open $backup_file for acrhiving
  597. if ($result === true) {
  598. $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
  599. $result = $result && $zip->addFile($db_result, "mwp_db/" . basename($db_result)); // Tries to add db dump form mwp_db dir to $backup_file
  600. $result = $result && $zip->close(); // Tries to close $backup_file
  601. } else {
  602. $result = false;
  603. }
  604. return $result; // true if $backup_file iz zipped successfully, false if error is occured in zip process
  605. }
  606. /**
  607. * Zipping database dump and index.php in folder mwp_db by PclZip library.
  608. *
  609. * @param string $task_name the name of backup task
  610. * @param string $backup_file absolute path to zip file
  611. * @return bool is compress successful or not
  612. */
  613. function pclzip_backup_db($task_name, $backup_file) {
  614. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  615. define('PCLZIP_TEMPORARY_DIR', MWP_BACKUP_DIR . '/');
  616. require_once ABSPATH . '/wp-admin/includes/class-pclzip.php';
  617. $zip = new PclZip($backup_file);
  618. if ($disable_comp) {
  619. $result = $zip->add(MWP_BACKUP_DIR, PCLZIP_OPT_REMOVE_PATH, MWP_BACKUP_DIR, PCLZIP_OPT_NO_COMPRESSION) !== 0;
  620. } else {
  621. $result = $zip->add(MWP_BACKUP_DIR, PCLZIP_OPT_REMOVE_PATH, MWP_BACKUP_DIR) !== 0;
  622. }
  623. return $result;
  624. }
  625. /**
  626. * Zipping whole site root folder and append to backup file with database dump
  627. * by system zip command, requires zip installed on OS.
  628. *
  629. * @param string $task_name the name of backup task
  630. * @param string $backup_file absolute path to zip file
  631. * @param array $exclude array of files of folders to exclude, relative to site's root
  632. * @param array $include array of folders from site root which are included to backup (wp-admin, wp-content, wp-includes are default)
  633. * @return array|bool true if successful or an array with error message if not
  634. */
  635. function zip_backup($task_name, $backup_file, $exclude, $include) {
  636. global $zip_errors;
  637. $sys = substr(PHP_OS, 0, 3);
  638. //Exclude paths
  639. $exclude_data = "-x";
  640. $exclude_file_data = '';
  641. // TODO: Prevent to $exclude include blank string '', beacuse zip 12 error will be occured.
  642. if (!empty($exclude)) {
  643. foreach ($exclude as $data) {
  644. if (is_dir(ABSPATH . $data)) {
  645. if ($sys == 'WIN')
  646. $exclude_data .= " $data/*.*";
  647. else
  648. $exclude_data .= " '$data/*'";
  649. } else {
  650. if ($sys == 'WIN'){
  651. if(file_exists(ABSPATH . $data)){
  652. $exclude_data .= " $data";
  653. $exclude_file_data .= " $data";
  654. }
  655. } else {
  656. if(file_exists(ABSPATH . $data)){
  657. $exclude_data .= " '$data'";
  658. $exclude_file_data .= " '$data'";
  659. }
  660. }
  661. }
  662. }
  663. }
  664. if($exclude_file_data){
  665. $exclude_file_data = "-x".$exclude_file_data;
  666. }
  667. //Include paths by default
  668. $add = array(
  669. trim(WPINC),
  670. trim(basename(WP_CONTENT_DIR)),
  671. "wp-admin"
  672. );
  673. $include_data = ". -i";
  674. foreach ($add as $data) {
  675. if ($sys == 'WIN')
  676. $include_data .= " $data/*.*";
  677. else
  678. $include_data .= " '$data/*'";
  679. }
  680. //Additional includes?
  681. if (!empty($include)) {
  682. foreach ($include as $data) {
  683. if ($data) {
  684. if ($sys == 'WIN')
  685. $include_data .= " $data/*.*";
  686. else
  687. $include_data .= " '$data/*'";
  688. }
  689. }
  690. }
  691. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  692. $comp_level = $disable_comp ? '-0' : '-1';
  693. $zip = $this->get_zip();
  694. chdir(ABSPATH);
  695. ob_start();
  696. $command = "$zip -q -j $comp_level $backup_file .* * $exclude_file_data";
  697. $this->_log("Executing $command");
  698. $result_f = $this->mmb_exec($command, false, true);
  699. if (!$result_f || $result_f == 18) { // disregard permissions error, file can't be accessed
  700. $command = "$zip -q -r $comp_level $backup_file $include_data $exclude_data";
  701. $result_d = $this->mmb_exec($command, false, true);
  702. $this->_log("Executing $command");
  703. if ($result_d && $result_d != 18) {
  704. @unlink($backup_file);
  705. if ($result_d > 0 && $result_d < 18)
  706. return array(
  707. 'error' => 'Failed to archive files (' . $zip_errors[$result_d] . ') .'
  708. );
  709. else {
  710. if ($result_d === -1) return false;
  711. return array(
  712. 'error' => 'Failed to archive files.'
  713. );
  714. }
  715. }
  716. } else {
  717. return false;
  718. }
  719. ob_get_clean();
  720. return true;
  721. }
  722. /**
  723. * Zipping whole site root folder and append to backup file with database dump
  724. * by ZipArchive class, requires php zip extension.
  725. *
  726. * @param string $task_name the name of backup task
  727. * @param string $backup_file absolute path to zip file
  728. * @param array $exclude array of files of folders to exclude, relative to site's root
  729. * @param array $include array of folders from site root which are included to backup (wp-admin, wp-content, wp-includes are default)
  730. * @return array|bool true if successful or an array with error message if not
  731. */
  732. function zip_archive_backup($task_name, $backup_file, $exclude, $include, $overwrite = false) {
  733. $filelist = $this->get_backup_files($exclude, $include);
  734. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  735. if (!$disable_comp) {
  736. $this->_log("Compression is not supported by ZipArchive");
  737. }
  738. $zip = new ZipArchive();
  739. if ($overwrite) {
  740. $result = $zip->open($backup_file, ZipArchive::OVERWRITE); // Tries to open $backup_file for acrhiving
  741. } else {
  742. $result = $zip->open($backup_file); // Tries to open $backup_file for acrhiving
  743. }
  744. if ($result === true) {
  745. foreach ($filelist as $file) {
  746. $result = $result && $zip->addFile($file, sprintf("%s", str_replace(ABSPATH, '', $file))); // Tries to add a new file to $backup_file
  747. }
  748. $result = $result && $zip->close(); // Tries to close $backup_file
  749. } else {
  750. $result = false;
  751. }
  752. return $result; // true if $backup_file iz zipped successfully, false if error is occured in zip process
  753. }
  754. /**
  755. * Zipping whole site root folder and append to backup file with database dump
  756. * by PclZip library.
  757. *
  758. * @param string $task_name the name of backup task
  759. * @param string $backup_file absolute path to zip file
  760. * @param array $exclude array of files of folders to exclude, relative to site's root
  761. * @param array $include array of folders from site root which are included to backup (wp-admin, wp-content, wp-includes are default)
  762. * @return array|bool true if successful or an array with error message if not
  763. */
  764. function pclzip_backup($task_name, $backup_file, $exclude, $include) {
  765. define('PCLZIP_TEMPORARY_DIR', MWP_BACKUP_DIR . '/');
  766. require_once ABSPATH . '/wp-admin/includes/class-pclzip.php';
  767. $zip = new PclZip($backup_file);
  768. $add = array(
  769. trim(WPINC),
  770. trim(basename(WP_CONTENT_DIR)),
  771. "wp-admin"
  772. );
  773. $include_data = array();
  774. if (!empty($include)) {
  775. foreach ($include as $data) {
  776. if ($data && file_exists(ABSPATH . $data))
  777. $include_data[] = ABSPATH . $data . '/';
  778. }
  779. }
  780. $include_data = array_merge($add, $include_data);
  781. if ($handle = opendir(ABSPATH)) {
  782. while (false !== ($file = readdir($handle))) {
  783. if ($file != "." && $file != ".." && !is_dir($file) && file_exists(ABSPATH . $file)) {
  784. $include_data[] = ABSPATH . $file;
  785. }
  786. }
  787. closedir($handle);
  788. }
  789. $disable_comp = $this->tasks[$task_name]['task_args']['disable_comp'];
  790. if ($disable_comp) {
  791. $result = $zip->add($include_data, PCLZIP_OPT_REMOVE_PATH, ABSPATH, PCLZIP_OPT_NO_COMPRESSION) !== 0;
  792. } else {
  793. $result = $zip->add($include_data, PCLZIP_OPT_REMOVE_PATH, ABSPATH) !== 0;
  794. }
  795. $exclude_data = array();
  796. if (!empty($exclude)) {
  797. foreach ($exclude as $data) {
  798. if (file_exists(ABSPATH . $data)) {
  799. if (is_dir(ABSPATH . $data))
  800. $exclude_data[] = $data . '/';
  801. else
  802. $exclude_data[] = $data;
  803. }
  804. }
  805. }
  806. $result = $result && $zip->delete(PCLZIP_OPT_BY_NAME, $exclude_data);
  807. return $result;
  808. }
  809. /**
  810. * Gets an array of relative paths of all files in site root recursively.
  811. * By default, there are all files from root folder, all files from folders wp-admin, wp-content, wp-includes recursively.
  812. * Parameter $include adds other folders from site root, and excludes any file or folder by relative path to site's root.
  813. *
  814. * @param array $exclude array of files of folders to exclude, relative to site's root
  815. * @param array $include array of folders from site root which are included to backup (wp-admin, wp-content, wp-includes are default)
  816. * @return array array with all files in site root dir
  817. */
  818. function get_backup_files($exclude, $include) {
  819. $add = array(
  820. trim(WPINC),
  821. trim(basename(WP_CONTENT_DIR)),
  822. "wp-admin"
  823. );
  824. $include = array_merge($add, $include);
  825. $filelist = array();
  826. if ($handle = opendir(ABSPATH)) {
  827. while (false !== ($file = readdir($handle))) {
  828. if (is_dir($file) && file_exists(ABSPATH . $file) && !(in_array($file, $include))) {
  829. $exclude[] = $file;
  830. }
  831. }
  832. closedir($handle);
  833. }
  834. $filelist = get_all_files_from_dir(ABSPATH, $exclude);
  835. return $filelist;
  836. }
  837. /**
  838. * Backup a database dump of WordPress site.
  839. * All backups are compressed by zip and placed in wp-content/managewp/backups folder.
  840. *
  841. * @param string $task_name the name of backup task, which backup is done
  842. * @param string $backup_file relative path to file which backup is stored
  843. * @return bool|array true if backup is successful, or an array with error message if is failed
  844. */
  845. function backup_db_compress($task_name, $backup_file) {
  846. $this->update_status($task_name, $this->statuses['db_dump']);
  847. $db_result = $this->backup_db();
  848. if ($db_result == false) {
  849. return array(
  850. 'error' => 'Failed to backup database.'
  851. );
  852. } else if (is_array($db_result) && isset($db_result['error'])) {
  853. return array(
  854. 'error' => $db_result['error']
  855. );
  856. }
  857. $this->update_status($task_name, $this->statuses['db_dump'], true);
  858. $this->update_status($task_name, $this->statuses['db_zip']);
  859. @file_put_contents(MWP_BACKUP_DIR.'/mwp_db/index.php', '');
  860. $zip_db_result = $this->zip_backup_db($task_name, $backup_file);
  861. if (!$zip_db_result) {
  862. $zip_archive_db_result = false;
  863. if (class_exists("ZipArchive")) {
  864. $this->_log("DB zip, fallback to ZipArchive");
  865. $zip_archive_db_result = $this->zip_archive_backup_db($task_name, $db_result, $backup_file);
  866. }
  867. if (!$zip_archive_db_result) {
  868. $this->_log("DB zip, fallback to PclZip");
  869. $pclzip_db_result = $this->pclzip_backup_db($task_name, $backup_file);
  870. if (!$pclzip_db_result) {
  871. @unlink(MWP_BACKUP_DIR.'/mwp_db/index.php');
  872. @unlink($db_result);
  873. @rmdir(MWP_DB_DIR);
  874. return array(
  875. 'error' => 'Failed to zip database. pclZip error (' . $archive->error_code . '): .' . $archive->error_string
  876. );
  877. }
  878. }
  879. }
  880. @unlink(MWP_BACKUP_DIR.'/mwp_db/index.php');
  881. @unlink($db_result);
  882. @rmdir(MWP_DB_DIR);
  883. $this->update_status($task_name, $this->statuses['db_zip'], true);
  884. return true;
  885. }
  886. /**
  887. * Creates database dump and places it in mwp_db folder in site's root.
  888. * This function dispatches if OS mysql command does not work calls a php alternative.
  889. *
  890. * @return string|array path to dump file if successful, or an array with error message if is failed
  891. */
  892. function backup_db() {
  893. $db_folder = MWP_DB_DIR . '/';
  894. if (!file_exists($db_folder)) {
  895. if (!mkdir($db_folder, 0755, true))
  896. return array(
  897. 'error' => 'Error creating database backup folder (' . $db_folder . '). Make sure you have corrrect write permissions.'
  898. );
  899. }
  900. $file = $db_folder . DB_NAME . '.sql';
  901. $result = $this->backup_db_dump($file); // try mysqldump always then fallback to php dump
  902. return $result;
  903. }
  904. /**
  905. * Creates database dump by system mysql command.
  906. *
  907. * @param string $file absolute path to file in which dump should be placed
  908. * @return string|array path to dump file if successful, or an array with error message if is failed
  909. */
  910. function backup_db_dump($file) {
  911. global $wpdb;
  912. $paths = $this->check_mysql_paths();
  913. $brace = (substr(PHP_OS, 0, 3) == 'WIN') ? '"' : '';
  914. $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;
  915. ob_start();
  916. $result = $this->mmb_exec($command);
  917. ob_get_clean();
  918. if (!$result) { // Fallback to php
  919. $this->_log("DB dump fallback to php");
  920. $result = $this->backup_db_php($file);
  921. return $result;
  922. }
  923. if (filesize($file) == 0 || !is_file($file) || !$result) {
  924. @unlink($file);
  925. return false;
  926. } else {
  927. return $file;
  928. }
  929. }
  930. /**
  931. * Creates database dump by php functions.
  932. *
  933. * @param string $file absolute path to file in which dump should be placed
  934. * @return string|array path to dump file if successful, or an array with error message if is failed
  935. */
  936. function backup_db_php($file) {
  937. global $wpdb;
  938. $tables = $wpdb->get_results('SHOW TABLES', ARRAY_N);
  939. foreach ($tables as $table) {
  940. //drop existing table
  941. $dump_data = "DROP TABLE IF EXISTS $table[0];";
  942. file_put_contents($file, $dump_data, FILE_APPEND);
  943. //create table
  944. $create_table = $wpdb->get_row("SHOW CREATE TABLE $table[0]", ARRAY_N);
  945. $dump_data = "\n\n" . $create_table[1] . ";\n\n";
  946. file_put_contents($file, $dump_data, FILE_APPEND);
  947. $count = $wpdb->get_var("SELECT count(*) FROM $table[0]");
  948. if ($count > 100)
  949. $count = ceil($count / 100);
  950. else if ($count > 0)
  951. $count = 1;
  952. for ($i = 0; $i < $count; $i++) {
  953. $low_limit = $i * 100;
  954. $qry = "SELECT * FROM $table[0] LIMIT $low_limit, 100";
  955. $rows = $wpdb->get_results($qry, ARRAY_A);
  956. if (is_array($rows)) {
  957. foreach ($rows as $row) {
  958. //insert single row
  959. $dump_data = "INSERT INTO $table[0] VALUES(";
  960. $num_values = count($row);
  961. $j = 1;
  962. foreach ($row as $value) {
  963. $value = addslashes($value);
  964. $value = preg_replace("/\n/Ui", "\\n", $value);
  965. $num_values == $j ? $dump_data .= "'" . $value . "'" : $dump_data .= "'" . $value . "', ";
  966. $j++;
  967. unset($value);
  968. }
  969. $dump_data .= ");\n";
  970. file_put_contents($file, $dump_data, FILE_APPEND);
  971. }
  972. }
  973. }
  974. $dump_data = "\n\n\n";
  975. file_put_contents($file, $dump_data, FILE_APPEND);
  976. unset($rows);
  977. unset($dump_data);
  978. }
  979. if (filesize($file) == 0 || !is_file($file)) {
  980. @unlink($file);
  981. return array(
  982. 'error' => 'Database backup failed. Try to enable MySQL dump on your server.'
  983. );
  984. }
  985. return $file;
  986. }
  987. /**
  988. * Restores full WordPress site or database only form backup zip file.
  989. *
  990. * @param array array of arguments passed to backup restore
  991. * [task_name] -> name of backup task
  992. * [result_id] -> id of baskup task result, which should be restored
  993. * [google_drive_token] -> json of Google Drive token, if it is remote destination
  994. * @return bool|array true if successful, or an array with error message if is failed
  995. */
  996. function restore($args) {
  997. global $wpdb;
  998. if (empty($args)) {
  999. return false;
  1000. }
  1001. extract($args);
  1002. if (isset($google_drive_token)) {
  1003. $this->tasks[$task_name]['task_args']['account_info']['mwp_google_drive']['google_drive_token'] = $google_drive_token;
  1004. }
  1005. $this->set_memory();
  1006. $unlink_file = true; //Delete file after restore
  1007. //Detect source
  1008. if ($backup_url) {
  1009. //This is for clone (overwrite)
  1010. include_once ABSPATH . 'wp-admin/includes/file.php';
  1011. $backup_file = download_url($backup_url);
  1012. if (is_wp_error($backup_file)) {
  1013. return array(
  1014. 'error' => 'Unable to download backup file ('.$backup_file->get_error_message().')'
  1015. );
  1016. }
  1017. $what = 'full';
  1018. } else {
  1019. $tasks = $this->tasks;
  1020. $task = $tasks[$task_name];
  1021. if (isset($task['task_results'][$result_id]['server'])) {
  1022. $backup_file = $task['task_results'][$result_id]['server']['file_path'];
  1023. $unlink_file = false; //Don't delete file if stored on server
  1024. } elseif (isset($task['task_results'][$result_id]['ftp'])) {
  1025. $ftp_file = $task['task_results'][$result_id]['ftp'];
  1026. $args = $task['task_args']['account_info']['mwp_ftp'];
  1027. $args['backup_file'] = $ftp_file;
  1028. $backup_file = $this->get_ftp_backup($args);
  1029. if ($backup_file == false) {
  1030. return array(
  1031. 'error' => 'Failed to download file from FTP.'
  1032. );
  1033. }
  1034. } elseif (isset($task['task_results'][$result_id]['amazons3'])) {
  1035. $amazons3_file = $task['task_results'][$result_id]['amazons3'];
  1036. $args = $task['task_args']['account_info']['mwp_amazon_s3'];
  1037. $args['backup_file'] = $amazons3_file;
  1038. $backup_file = $this->get_amazons3_backup($args);
  1039. if ($backup_file == false) {
  1040. return array(
  1041. 'error' => 'Failed to download file from Amazon S3.'
  1042. );
  1043. }
  1044. } elseif(isset($task['task_results'][$result_id]['dropbox'])){
  1045. $dropbox_file = $task['task_results'][$result_id]['dropbox'];
  1046. $args = $task['task_args']['account_info']['mwp_dropbox'];
  1047. $args['backup_file'] = $dropbox_file;
  1048. $backup_file = $this->get_dropbox_backup($args);
  1049. if ($backup_file == false) {
  1050. return array(
  1051. 'error' => 'Failed to download file from Dropbox.'
  1052. );
  1053. }
  1054. } elseif (isset($task['task_results'][$result_id]['google_drive'])) {
  1055. $google_drive_file = $task['task_results'][$result_id]['google_drive'];
  1056. $args = $task['task_args']['account_info']['mwp_google_drive'];
  1057. $args['backup_file'] = $google_drive_file;
  1058. $backup_file = $this->get_google_drive_backup($args);
  1059. if (is_array($backup_file) && isset($backup_file['error'])) {
  1060. return array(
  1061. 'error' => 'Failed to download file from Google Drive, reason: ' . $backup_file['error']
  1062. );
  1063. } elseif ($backup_file == false) {
  1064. return array(
  1065. 'error' => 'Failed to download file from Google Drive.'
  1066. );
  1067. }
  1068. }
  1069. $what = $tasks[$task_name]['task_args']['what'];
  1070. }
  1071. $this->wpdb_reconnect();
  1072. if ($backup_file && file_exists($backup_file)) {
  1073. if ($overwrite) {
  1074. //Keep old db credentials before overwrite
  1075. if (!copy(ABSPATH . 'wp-config.php', ABSPATH . 'mwp-temp-wp-config.php')) {
  1076. @unlink($backup_file);
  1077. return array(
  1078. 'error' => 'Error creating wp-config. Please check your write permissions.'
  1079. );
  1080. }
  1081. $db_host = DB_HOST;
  1082. $db_user = DB_USER;
  1083. $db_password = DB_PASSWORD;
  1084. $home = rtrim(get_option('home'), "/");
  1085. $site_url = get_option('site_url');
  1086. $clone_options = array();
  1087. if (trim($clone_from_url) || trim($mwp_clone)) {
  1088. $clone_options['_worker_nossl_key'] = get_option('_worker_nossl_key');
  1089. $clone_options['_worker_public_key'] = get_option('_worker_public_key');
  1090. $clone_options

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