PluginProbe
Database Backup for WordPress / trunk
Database Backup for WordPress vtrunk
trunk 1.3 1.4 2.0 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.3.1 2.3.3 2.4 2.5 All 28 releases
wp-db-backup / wp-db-backup.php

wp-db-backup.php in Database Backup for WordPress trunk, at wp-db-backup.php

1,854 lines 58.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Database Backup for WordPress
4 Plugin URI: https://github.com/deliciousbrains/wp-db-backup
5 Description: On-demand backup of your WordPress database. Navigate to <a href="edit.php?page=wp-db-backup">Tools &rarr; Backup</a> to get started.
6 Author: Delicious Brains
7 Author URI: https://deliciousbrains.com
8 Version: 2.5.3
9 Domain Path: /languages
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
24 */
25
26 if ( ! defined( 'ABSPATH' ) ) {
27 die( 'Please do not load this file directly.' );
28 }
29
30 if ( ! defined( 'DBBWP_ROWS_PER_SEGMENT' ) ) {
31 define( 'DBBWP_ROWS_PER_SEGMENT', 100 );
32 }
33
34 /**
35 * Set DBBWP_MOD_EVASIVE_OVERRIDE to true
36 * and increase DBBWP_MOD_EVASIVE_DELAY
37 * if the backup stops prematurely.
38 */
39 // define('DBBWP_MOD_EVASIVE_OVERRIDE', false);
40 if ( ! defined( 'DBBWP_MOD_EVASIVE_DELAY' ) ) {
41 define( 'DBBWP_MOD_EVASIVE_DELAY', '500' );
42 }
43
44 class wpdbBackup {
45
46 private $backup_complete = false;
47 private $backup_file = '';
48 private $backup_filename;
49 private $core_table_names = array();
50 private $errors = array();
51 private $backup_dir;
52 private $basename;
53 private $fp;
54 private $page_url;
55 private $referer_check_key;
56 private $version = '2.5.3';
57
58 function module_check() {
59 $mod_evasive = false;
60
61 if ( defined( 'DBBWP_MOD_EVASIVE_OVERRIDE' ) && true === DBBWP_MOD_EVASIVE_OVERRIDE ) {
62 return true;
63 }
64
65 if ( ! defined( 'DBBWP_MOD_EVASIVE_OVERRIDE' ) || false === DBBWP_MOD_EVASIVE_OVERRIDE ) {
66 return false;
67 }
68
69 if ( function_exists( 'apache_get_modules' ) ) {
70 foreach ( (array) apache_get_modules() as $mod ) {
71 if ( false !== strpos( $mod, 'mod_evasive' ) || false !== strpos( $mod, 'mod_dosevasive' ) ) {
72 return true;
73 }
74 }
75 }
76
77 return false;
78 }
79
80 function __construct() {
81 global $table_prefix, $wpdb;
82
83 add_action( 'wp_ajax_save_backup_time', array( &$this, 'save_backup_time' ) );
84 add_action( 'init', array( &$this, 'init_textdomain' ) );
85 add_action( 'init', array( &$this, 'set_page_url' ) );
86 add_action( 'admin_notices', array( &$this, 'update_notice' ) );
87 add_action( 'wp_db_backup_cron', array( &$this, 'cron_backup' ) );
88 add_action( 'wp_cron_daily', array( &$this, 'wp_cron_daily' ) );
89 add_filter( 'cron_schedules', array( &$this, 'add_sched_options' ) );
90 add_filter( 'wp_db_b_schedule_choices', array( &$this, 'schedule_choices' ) );
91
92 $table_prefix = ( isset( $table_prefix ) ) ? $table_prefix : $wpdb->prefix;
93 $datum = date( 'Ymd_B' );
94 $nonce = wp_generate_password( 12, false );
95
96 $this->backup_filename = sanitize_text_field( DB_NAME . '_' . $table_prefix . $datum . '_' . $nonce . '.sql' );
97
98 $possible_names = array(
99 'categories',
100 'commentmeta',
101 'comments',
102 'link2cat',
103 'linkcategories',
104 'links',
105 'options',
106 'post2cat',
107 'postmeta',
108 'posts',
109 'terms',
110 'term_taxonomy',
111 'term_relationships',
112 'termmeta',
113 'users',
114 'usermeta',
115 );
116
117 foreach ( $possible_names as $name ) {
118 if ( isset( $wpdb->{$name} ) ) {
119 $this->core_table_names[] = $wpdb->{$name};
120 }
121 }
122
123 $tmp_dir = get_temp_dir();
124
125 $this->backup_dir = trailingslashit( apply_filters( 'wp_db_b_backup_dir', $tmp_dir ) );
126 $this->basename = 'wp-db-backup';
127
128 $this->referer_check_key = $this->basename . '-download_' . DB_NAME;
129 if ( isset( $_POST['do_backup'] ) ) {
130 $this->wp_secure( 'fatal' );
131 check_admin_referer( $this->referer_check_key );
132 $this->can_user_backup( 'main' );
133
134 // save exclude prefs
135 update_option(
136 'wp_db_backup_excs',
137 array(
138 'revisions' => $this->get_revisions_to_exclude(),
139 'spam' => $this->get_spam_to_exclude(),
140 )
141 );
142 switch ( $_POST['do_backup'] ) {
143 case 'backup':
144 add_action( 'init', array( &$this, 'perform_backup' ) );
145 break;
146 case 'fragments':
147 add_action( 'admin_menu', array( &$this, 'admin_menu' ) );
148 break;
149 }
150 } elseif ( isset( $_GET['fragment'] ) ) {
151 if ( ! $this->can_user_backup( 'frame' ) ) {
152 return;
153 }
154 add_action( 'init', array( &$this, 'init' ) );
155 } elseif ( isset( $_GET['backup'] ) ) {
156 if ( ! $this->can_user_backup() ) {
157 return;
158 }
159 add_action( 'init', array( &$this, 'init' ) );
160 } else {
161 add_action( 'admin_menu', array( &$this, 'admin_menu' ) );
162 }
163 }
164
165 function init() {
166 $this->can_user_backup();
167 if ( isset( $_GET['backup'] ) ) {
168 $via = isset( $_GET['via'] ) ? sanitize_text_field( $_GET['via'] ) : 'http';
169
170 $this->backup_file = sanitize_text_field( $_GET['backup'] );
171 $this->validate_file( $this->backup_file );
172
173 switch ( $via ) {
174 case 'smtp':
175 case 'email':
176 $success = $this->deliver_backup( $this->backup_file, 'smtp', sanitize_text_field( $_GET['recipient'] ), 'frame' );
177 $this->error_display( 'frame' );
178 if ( $success ) {
179 echo '
180 <!-- ' . $via . ' -->
181 <script type="text/javascript"><!--\\
182 ';
183 echo '
184 alert("' . __( 'Backup Complete!', 'wp-db-backup' ) . '");
185 window.onbeforeunload = null;
186 </script>
187 ';
188 }
189 break;
190 default:
191 $success = $this->deliver_backup( $this->backup_file, $via );
192 echo $this->error_display( 'frame', false );
193
194 if ( $success ) {
195 echo '
196 <script type="text/javascript">
197 window.parent.setProgress("' . __( 'Backup Complete!', 'wp-db-backup' ) . '");
198 </script>
199 ';
200 }
201 }
202 exit;
203 }
204
205 if ( isset( $_GET['fragment'] ) ) {
206 list($table, $segment, $filename) = explode( ':', sanitize_text_field( $_GET['fragment'] ) );
207
208 if ( empty( $table ) || in_array( $table, $this->get_tables() ) ) {
209 $this->validate_file( $filename );
210 $this->backup_fragment( $table, $segment, $filename );
211 } else {
212 $this->error(
213 array(
214 'loc' => 'frame',
215 'kind' => 'fatal',
216 'msg' => __(
217 'There was an error determining the table to backup. Please check the settings used for the backup and try again.',
218 'wp-db-backup'
219 ),
220 )
221 );
222 }
223 }
224
225 die();
226 }
227
228 function init_textdomain() {
229 load_plugin_textdomain(
230 'wp-db-backup',
231 false,
232 dirname( plugin_basename( __FILE__ ) ) . '/languages'
233 );
234 }
235
236 function set_page_url() {
237 $query_args = array( 'page' => $this->basename );
238
239 if ( function_exists( 'wp_create_nonce' ) ) {
240 $query_args = array_merge( $query_args, array( '_wpnonce' => wp_create_nonce( $this->referer_check_key ) ) );
241 }
242
243 $base = ( function_exists( 'site_url' ) ) ? site_url( '', 'admin' ) : get_option( 'siteurl' );
244 $this->page_url = add_query_arg( $query_args, $base . '/wp-admin/edit.php' );
245 }
246
247 /*
248 * Add a link to back up your database when doing a core upgrade.
249 */
250 function update_notice() {
251 global $pagenow;
252
253 if ( empty( $pagenow ) || 'update-core.php' !== $pagenow ) {
254 return false;
255 }
256 ?>
257 <div class="notice notice-warning">
258 <p>
259 <?php
260 printf(
261 __( 'Click <a href="%s">here</a> to back up your database using the WordPress Database Backup plugin. <strong>Note:</strong> WordPress Database Backup does <em>not</em> back up your files, just your database.', 'wp-db-backup' ),
262 esc_url( get_admin_url( null, 'tools.php?page=wp-db-backup' ) )
263 );
264 ?>
265 </p>
266 </div>
267 <?php
268 }
269
270 function build_backup_script() {
271 global $table_prefix, $wpdb;
272
273 echo '<fieldset class="options backup-running"><legend>' . __( 'Progress', 'wp-db-backup' ) . '</legend>
274
275 <div class="panel-heading">
276 <h3>Backup In Progress...</h3>
277 </div>
278
279 <div class="panel-content">
280 <div class="progress-bar">
281 <div id="progress-status"></div>
282 <div id="meterbox" style="height:11px;width:80%;padding:3px;border:1px solid #659fff;"><div id="meter" style="color:#fff;height:11px;line-height:11px;background-color:#659fff;width:0%;text-align:center;font-size:6pt;"></div></div>
283 <div id="progress_message"></div>'?>
284 </div>
285
286 <div class="info-notice">
287 <img src="<?php echo plugin_dir_url( __FILE__ ) . 'assets/warning.svg'; ?>">
288 <p>
289 Whilst the backup is in progress, please do not close the browser, reload or change the page, or click the stop or back browser buttons. This would result in the backup failing.
290 </p>
291 </div>
292
293 <?php echo '<div id="errors"></div>
294 <iframe id="backuploader" src="about:blank" style="display:none;border:none;height:1em;width:1px;"></iframe>
295 </fieldset>
296 <script type="text/javascript">
297 //<![CDATA[
298 window.onbeforeunload = function() {
299 return "' . __( 'Navigating away from this page will cause your backup to fail.', 'wp-db-backup' ) . '";
300 }
301 function setMeter(pct) {
302 var meterStatus = document.getElementById("progress-status");
303 var meter = document.getElementById("meter");
304 meter.style.width = pct + "%";
305 meterStatus.innerHTML = Math.floor(pct) + "%";
306 }
307 function setProgress(str) {
308 var progress = document.getElementById("progress_message");
309 progress.innerHTML = str;
310 }
311 function addError(str) {
312 var errors = document.getElementById("errors");
313 errors.innerHTML = errors.innerHTML + str + "<br />";
314 }
315
316 function backup(table, segment) {
317 var fram = document.getElementById("backuploader");
318 fram.src = "' . $this->page_url . '&fragment=" + table + ":" + segment + ":' . $this->backup_filename . ':";
319 }
320
321 var curStep = 0;
322
323 function nextStep() {
324 backupStep(curStep);
325 curStep++;
326 }
327
328 function finishBackup() {
329 var fram = document.getElementById("backuploader");
330 setMeter(100);
331 ';
332
333 $download_uri = add_query_arg( 'backup', $this->backup_filename, $this->page_url );
334 switch ( $_POST['deliver'] ) {
335 case 'http':
336 echo '
337 setProgress("' . __( 'Preparing download.', 'wp-db-backup' ) . '");
338 window.onbeforeunload = null;
339 fram.src = "' . $download_uri . '";
340
341 setTimeout( function() {
342 var secondFrame = document.createElement("iframe");
343 secondFrame.style.display = "none";
344 fram.parentNode.insertBefore(secondFrame, fram);
345 secondFrame.src = "' . $download_uri . '&download-retry=1";
346 }, 30000 );
347 ';
348 break;
349 case 'smtp':
350 $email = sanitize_text_field( wp_unslash( $_POST['backup_recipient'] ) );
351 if ( get_option( 'wpdb_backup_recip' ) != $email ) {
352 update_option( 'wpdb_backup_recip', $email );
353 }
354 echo '
355 setProgress("' . sprintf( __( 'Your backup has been emailed to %s', 'wp-db-backup' ), $email ) . '");
356 window.onbeforeunload = null;
357 fram.src = "' . $download_uri . '&via=email&recipient=' . $email . '";
358 ';
359 break;
360 default:
361 echo '
362 setProgress("' . __( 'Backup Complete!', 'wp-db-backup' ) . '");
363 window.onbeforeunload = null;
364 ';
365 }
366
367 echo '
368 }
369
370 function backupStep(step) {
371 switch(step) {
372 case 0: backup("", 0); break;
373 ';
374
375 $also_backup = $this->get_post_data_array( 'other_tables' );
376 $core_tables = $this->get_post_data_array( 'core_tables' );
377 $tables = array_merge( $core_tables, $also_backup );
378 $step_count = 1;
379
380 foreach ( $tables as $table ) {
381 $rec_count = $wpdb->get_var( "SELECT count(*) FROM {$table}" );
382 $rec_segments = ceil( $rec_count / DBBWP_ROWS_PER_SEGMENT );
383 $table_count = 0;
384 if ( $this->module_check() ) {
385 $delay = "setTimeout('";
386 $delay_time = "', " . (int) DBBWP_MOD_EVASIVE_DELAY . ')';
387 } else {
388 $delay = $delay_time = ''; }
389 do {
390 echo "case {$step_count}: {$delay}backup(\"{$table}\", {$table_count}){$delay_time}; break;\n";
391 $step_count++;
392 $table_count++;
393 } while ( $table_count < $rec_segments );
394 echo "case {$step_count}: {$delay}backup(\"{$table}\", -1){$delay_time}; break;\n";
395 $step_count++;
396 }
397
398 echo "case {$step_count}: finishBackup(); break;";
399 echo '
400 }
401 if(step != 0) setMeter(100 * step / ' . $step_count . ');
402 }
403
404 nextStep();
405 // ]]>
406 </script>
407 ';
408 }
409
410 function backup_fragment( $table, $segment, $filename ) {
411 global $table_prefix, $wpdb;
412
413 echo "$table:$segment:$filename";
414
415 if ( $table == '' ) {
416 $msg = __( 'Creating backup file...', 'wp-db-backup' );
417 } else {
418 if ( $segment == -1 ) {
419 $msg = sprintf( __( 'Finished backing up table \\"%s\\".', 'wp-db-backup' ), $table );
420 } else {
421 $msg = sprintf( __( 'Backing up table \\"%s\\"...', 'wp-db-backup' ), $table );
422 }
423 }
424
425 if ( wp_is_writable( $this->backup_dir ) ) {
426 $this->fp = $this->open( $this->backup_dir . $filename, 'a' );
427 if ( ! $this->fp ) {
428 $this->error( __( 'Could not open the backup file for writing!', 'wp-db-backup' ) );
429 $this->error(
430 array(
431 'loc' => 'frame',
432 'kind' => 'fatal',
433 'msg' => __(
434 'The backup file could not be saved. Please check the permissions for writing to your backup directory and try again.',
435 'wp-db-backup'
436 ),
437 )
438 );
439 } else {
440 if ( $table == '' ) {
441 //Begin new backup of MySql
442 $this->stow( '# ' . __( 'WordPress MySQL database backup', 'wp-db-backup' ) . "\n" );
443 $this->stow( "#\n" );
444 $this->stow( '# ' . sprintf( __( 'Generated: %s', 'wp-db-backup' ), date( 'l j. F Y H:i T' ) ) . "\n" );
445 $this->stow( '# ' . sprintf( __( 'Hostname: %s', 'wp-db-backup' ), DB_HOST ) . "\n" );
446 $this->stow( '# ' . sprintf( __( 'Database: %s', 'wp-db-backup' ), $this->backquote( DB_NAME ) ) . "\n" );
447 $this->stow( "# --------------------------------------------------------\n" );
448 } else {
449 if ( $segment == 0 ) {
450 // Increase script execution time-limit to 15 min for every table.
451 if ( ! ini_get( 'safe_mode' ) ) {
452 @set_time_limit( 15 * 60 );
453 }
454 // Create the SQL statements
455 $this->stow( "# --------------------------------------------------------\n" );
456 $this->stow( '# ' . sprintf( __( 'Table: %s', 'wp-db-backup' ), $this->backquote( $table ) ) . "\n" );
457 $this->stow( "# --------------------------------------------------------\n" );
458 }
459 $this->backup_table( $table, $segment );
460 }
461 }
462 } else {
463 $this->error(
464 array(
465 'kind' => 'fatal',
466 'loc' => 'frame',
467 'msg' => __(
468 'The backup directory is not writeable! Please check the permissions for writing to your backup directory and try again.',
469 'wp-db-backup'
470 ),
471 )
472 );
473 }
474
475 if ( $this->fp ) {
476 $this->close( $this->fp );
477 }
478
479 $this->error_display( 'frame' );
480
481 echo '<script type="text/javascript"><!--//
482 var msg = "' . $msg . '";
483 window.parent.setProgress(msg);
484 window.parent.nextStep();
485 //--></script>
486 ';
487 die();
488 }
489
490 function perform_backup() {
491 // are we backing up any other tables?
492 $also_backup = array();
493 if ( isset( $_POST['other_tables'] ) ) {
494 $also_backup = sanitize_text_field( $_POST['other_tables'] );
495 }
496
497 $core_tables = sanitize_text_field( $_POST['core_tables'] );
498 $this->backup_file = $this->db_backup( $core_tables, $also_backup );
499
500 if ( false !== $this->backup_file ) {
501 if ( 'smtp' == $_POST['deliver'] ) {
502 $email = sanitize_text_field( wp_unslash( $_POST['backup_recipient'] ) );
503 $this->deliver_backup( $this->backup_file, sanitize_text_field( $_POST['deliver'] ), $email, 'main' );
504 if ( get_option( 'wpdb_backup_recip' ) != $email ) {
505 update_option( 'wpdb_backup_recip', $email );
506 }
507 wp_redirect( $this->page_url );
508 } elseif ( 'http' == $_POST['deliver'] ) {
509 $download_uri = add_query_arg( 'backup', $this->backup_file, $this->page_url );
510 wp_redirect( $download_uri );
511 exit;
512 }
513
514 // we do this to say we're done.
515 $this->backup_complete = true;
516 }
517 }
518
519 function admin_header() {
520 ?>
521 <script type="text/javascript">
522 //<![CDATA[
523 if ( 'undefined' != typeof addLoadEvent ) {
524 addLoadEvent(function() {
525 var t = {'extra-tables-list':{name: 'other_tables[]'}, 'include-tables-list':{name: 'wp_cron_backup_tables[]'}};
526
527 for ( var k in t ) {
528 t[k].s = null;
529 var d = document.getElementById(k);
530 if ( ! d )
531 continue;
532 var ul = d.getElementsByTagName('ul').item(0);
533 if ( ul ) {
534 var lis = ul.getElementsByTagName('li');
535 if ( 2 < lis.length ) {
536 var text = document.querySelector('.instructions-container p');
537 text.style.display = 'block';
538 }
539 }
540 t[k].p = d.getElementsByTagName("input");
541 for(var i=0; i < t[k].p.length; i++) {
542 if(t[k].name == t[k].p[i].getAttribute('name')) {
543 t[k].p[i].id = k + '-table-' + i;
544 var label = document.getElementById(t[k].p[i].id).parentNode;
545 t[k].p[i].onkeyup = label.onclick = function(e) {
546 e = e ? e : event;
547 if ( 16 == e.keyCode )
548 return;
549 var match = /([\w-]*)-table-(\d*)/.exec(this.querySelector('input').id);
550 var listname = match[1];
551 var that = match[2];
552 if ( null === t[listname].s )
553 t[listname].s = that;
554 else if ( e.shiftKey ) {
555 console.log(this);
556 var start = Math.min(that, t[listname].s) + 1;
557 var end = Math.max(that, t[listname].s);
558 this.querySelector('input').checked = true;
559 for( var j=start; j < end; j++)
560 t[listname].p[j].checked = t[listname].p[j].checked ? false : true;
561 t[listname].s = null;
562 }
563 }
564 }
565 }
566 }
567
568 <?php if ( function_exists( 'wp_schedule_event' ) ) : // needs to be at least WP 2.1 for ajax ?>
569 if ( 'undefined' == typeof XMLHttpRequest )
570 var xml = new ActiveXObject( navigator.userAgent.indexOf('MSIE 5') >= 0 ? 'Microsoft.XMLHTTP' : 'Msxml2.XMLHTTP' );
571 else
572 var xml = new XMLHttpRequest();
573
574 var initTimeChange = function() {
575 var timeWrap = document.getElementById('backup-time-wrap');
576 var backupTime = document.getElementById('next-backup-time');
577 if ( !! timeWrap && !! backupTime && ( 1 ==
578 <?php
579 echo (int) ( 'en' == strtolower( substr( get_locale(), 0, 2 ) ) );
580 ?>
581 ) ) {
582 var span = document.createElement('span');
583 span.className = 'submit';
584 span.id = 'change-wrap';
585 span.innerHTML = '<input type="submit" id="change-backup-time" name="change-backup-time" value="<?php _e( 'Change', 'wp-db-backup' ); ?>" />';
586 timeWrap.appendChild(span);
587 backupTime.ondblclick = function(e) { span.parentNode.removeChild(span); clickTime(e, backupTime); };
588 span.onclick = function(e) { span.parentNode.removeChild(span); clickTime(e, backupTime); };
589 }
590 }
591
592 var clickTime = function(e, backupTime) {
593 var tText = backupTime.innerHTML;
594 backupTime.innerHTML = '<input type="text" value="' + tText + '" name="backup-time-text" id="backup-time-text" /> <span class="submit"><input type="submit" name="save-backup-time" id="save-backup-time" value="<?php _e( 'Save', 'wp-db-backup' ); ?>" /></span>';
595 backupTime.ondblclick = null;
596 var mainText = document.getElementById('backup-time-text');
597 mainText.focus();
598 var saveTButton = document.getElementById('save-backup-time');
599 if ( !! saveTButton )
600 saveTButton.onclick = function(e) { saveTime(backupTime, mainText); return false; };
601 if ( !! mainText )
602 mainText.onkeydown = function(e) {
603 e = e || window.event;
604 if ( 13 == e.keyCode ) {
605 saveTime(backupTime, mainText);
606 return false;
607 }
608 }
609 }
610
611 var saveTime = function(backupTime, mainText) {
612 var tVal = mainText.value;
613
614 xml.open('POST', 'admin-ajax.php', true);
615 xml.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
616 if ( xml.overrideMimeType )
617 xml.setRequestHeader('Connection', 'close');
618 xml.send('action=save_backup_time&_wpnonce=<?php echo wp_create_nonce( $this->referer_check_key ); ?>&backup-time='+tVal);
619 xml.onreadystatechange = function() {
620 if ( 4 == xml.readyState && '0' != xml.responseText ) {
621 backupTime.innerHTML = xml.responseText;
622 initTimeChange();
623 }
624 }
625 }
626
627 initTimeChange();
628 <?php endif; // wp_schedule_event exists ?>
629 });
630 }
631 //]]>
632 </script>
633 <?php
634 }
635
636 function admin_load() {
637 add_action( 'admin_head', array( &$this, 'admin_header' ) );
638 wp_enqueue_style( 'wp-db-backup-styles', plugin_dir_url( __FILE__ ) . 'assets/css/style.css', array( 'common', 'forms' ) );
639 wp_enqueue_script( 'wp-db-backup-script', plugin_dir_url( __FILE__ ) . 'assets/js/script.js', array( 'jquery' ), $this->version, true );
640 }
641
642 function admin_menu() {
643 $_page_hook = add_management_page( __( 'Backup', 'wp-db-backup' ), __( 'Backup', 'wp-db-backup' ), 'import', $this->basename, array( &$this, 'backup_menu' ) );
644 add_action( 'load-' . $_page_hook, array( &$this, 'admin_load' ) );
645 if ( function_exists( 'get_current_screen' ) ) {
646 $screen = convert_to_screen( $_page_hook );
647 if ( method_exists( $screen, 'add_help_tab' ) ) {
648 $screen->add_help_tab(
649 array(
650 'title' => __( 'Backup', 'wp-db-backup' ),
651 'id' => $_page_hook,
652 'content' => $this->help_menu(),
653 )
654 );
655 }
656 } elseif ( function_exists( 'add_contextual_help' ) ) {
657 $text = $this->help_menu();
658 add_contextual_help( $_page_hook, $text );
659 }
660 }
661
662 /**
663 * Add WP-DB-Backup-specific help options to the 2.7 =< WP contextual help menu
664 * @return string The text of the help menu.
665 */
666 function help_menu() {
667 $text = "\n<a href=\"http://wordpress.org/extend/plugins/wp-db-backup/faq/\" target=\"_blank\">" . __( 'FAQ', 'wp-db-backup' ) . '</a>';
668 return $text;
669 }
670
671 function save_backup_time() {
672 if ( $this->can_user_backup() ) {
673 // try to get a time from the input string
674 $time = strtotime( strval( $_POST['backup-time'] ) );
675 if ( ! empty( $time ) && time() < $time ) {
676 wp_clear_scheduled_hook( 'wp_db_backup_cron' ); // unschedule previous
677 $scheds = (array) wp_get_schedules();
678 $name = get_option( 'wp_cron_backup_schedule' );
679 if ( 0 != $time ) {
680 wp_schedule_event( $time, $name, 'wp_db_backup_cron' );
681 echo gmdate( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $time + ( get_option( 'gmt_offset' ) * 3600 ) );
682 exit;
683 }
684 }
685 } else {
686 die( 0 );
687 }
688 }
689
690 /**
691 * Better addslashes for SQL queries.
692 * Taken from phpMyAdmin.
693 */
694 function sql_addslashes( $a_string = '', $is_like = false ) {
695 if ( empty( $a_string ) ) {
696 return $a_string;
697 }
698
699 if ( $is_like ) {
700 $a_string = str_replace( '\\', '\\\\\\\\', $a_string );
701 } else {
702 $a_string = str_replace( '\\', '\\\\', $a_string );
703 }
704
705 return str_replace( '\'', '\\\'', $a_string );
706 }
707
708 /**
709 * Add backquotes to tables and db-names in
710 * SQL queries. Taken from phpMyAdmin.
711 */
712 function backquote( $a_name ) {
713 if ( ! empty( $a_name ) && $a_name != '*' ) {
714 if ( is_array( $a_name ) ) {
715 $result = array();
716 reset( $a_name );
717 while ( list($key, $val) = each( $a_name ) ) {
718 $result[ $key ] = '`' . $val . '`';
719 }
720 return $result;
721 } else {
722 return '`' . $a_name . '`';
723 }
724 } else {
725 return $a_name;
726 }
727 }
728
729 function open( $filename = '', $mode = 'w' ) {
730 if ( '' == $filename ) {
731 return false;
732 }
733 $fp = @fopen( $filename, $mode );
734 return $fp;
735 }
736
737 function close( $fp ) {
738 fclose( $fp );
739 }
740
741 /**
742 * Write to the backup file
743 * @param string $query_line the line to write
744 * @return null
745 */
746 function stow( $query_line ) {
747 if ( false === @fwrite( $this->fp, $query_line ) ) {
748 $this->error( __( 'There was an error writing a line to the backup script:', 'wp-db-backup' ) . ' ' . $query_line . ' ' . $php_errormsg );
749 }
750 }
751
752 /**
753 * Logs any error messages
754 * @param array $args
755 * @return bool
756 */
757 function error( $args = array() ) {
758 if ( is_string( $args ) ) {
759 $args = array( 'msg' => $args );
760 }
761
762 $args = array_merge(
763 array(
764 'loc' => 'main',
765 'kind' => 'warn',
766 'msg' => '',
767 ),
768 $args
769 );
770
771 $this->errors[ $args['kind'] ][] = $args['msg'];
772
773 if ( 'fatal' == $args['kind'] || 'frame' == $args['loc'] ) {
774 $this->error_display( $args['loc'] );
775 }
776
777 return true;
778 }
779
780 /**
781 * Displays error messages
782 * @param array $errs
783 * @param string $loc
784 * @return string
785 */
786 function error_display( $loc = 'main', $echo = true ) {
787 $errs = $this->errors;
788 unset( $this->errors );
789
790 if ( ! count( $errs ) ) {
791 return;
792 }
793
794 $msg = '';
795 $errs['fatal'] = isset( $errs['fatal'] ) ? (array) $errs['fatal'] : array();
796 $errs['warn'] = isset( $errs['warn'] ) ? (array) $errs['warn'] : array();
797 $err_list = array_slice( array_merge( $errs['fatal'], $errs['warn'] ), 0, 10 );
798
799 if ( 10 == count( $err_list ) ) {
800 $err_list[9] = __( 'Subsequent errors have been omitted from this log.', 'wp-db-backup' );
801 }
802
803 $wrap = ( 'frame' == $loc ) ? "<script type=\"text/javascript\">\n var msgList = ''; \n %1\$s \n if ( msgList ) alert(msgList); \n </script>" : '%1$s';
804 $line = ( 'frame' == $loc ) ?
805 "try{ window.parent.addError('%1\$s'); } catch(e) { msgList += ' %1\$s';}\n" :
806 "%1\$s<br />\n";
807
808 foreach ( (array) $err_list as $err ) {
809 $msg .= sprintf( $line, str_replace( array( "\n", "\r" ), '', addslashes( $err ) ) );
810 }
811
812 $msg = sprintf( $wrap, $msg );
813
814 if ( count( $errs['fatal'] ) ) {
815 if ( function_exists( 'wp_die' ) && 'frame' != $loc ) {
816 wp_die( stripslashes( $msg ) );
817 } else {
818 die( $msg );
819 }
820 } else {
821 if ( $echo ) {
822 echo $msg;
823 } else {
824 return $msg;
825 }
826 }
827 }
828
829 /**
830 * Taken partially from phpMyAdmin and partially from
831 * Alain Wolf, Zurich - Switzerland
832 * Website: http://restkultur.ch/personal/wolf/scripts/db_backup/
833
834 * Modified by Scott Merrill (http://www.skippy.net/)
835 * to use the WordPress $wpdb object
836 * @param string $table
837 * @param string $segment
838 * @return void
839 */
840 function backup_table( $table, $segment = 'none' ) {
841 global $wpdb;
842
843 $table_structure = $wpdb->get_results( "DESCRIBE $table" );
844 if ( ! $table_structure ) {
845 $this->error( __( 'Error getting table details', 'wp-db-backup' ) . ": $table" );
846 return false;
847 }
848
849 if ( ( $segment == 'none' ) || ( $segment == 0 ) ) {
850 // Add SQL statement to drop existing table
851 $this->stow( "\n\n" );
852 $this->stow( "#\n" );
853 $this->stow( '# ' . sprintf( __( 'Delete any existing table %s', 'wp-db-backup' ), $this->backquote( $table ) ) . "\n" );
854 $this->stow( "#\n" );
855 $this->stow( "\n" );
856 $this->stow( 'DROP TABLE IF EXISTS ' . $this->backquote( $table ) . ";\n" );
857
858 // Table structure
859 // Comment in SQL-file
860 $this->stow( "\n\n" );
861 $this->stow( "#\n" );
862 $this->stow( '# ' . sprintf( __( 'Table structure of table %s', 'wp-db-backup' ), $this->backquote( $table ) ) . "\n" );
863 $this->stow( "#\n" );
864 $this->stow( "\n" );
865
866 $create_table = $wpdb->get_results( "SHOW CREATE TABLE $table", ARRAY_N );
867 if ( false === $create_table ) {
868 $err_msg = sprintf( __( 'Error with SHOW CREATE TABLE for %s.', 'wp-db-backup' ), $table );
869 $this->error( $err_msg );
870 $this->stow( "#\n# $err_msg\n#\n" );
871 }
872 $this->stow( $create_table[0][1] . ' ;' );
873
874 if ( false === $table_structure ) {
875 $err_msg = sprintf( __( 'Error getting table structure of %s', 'wp-db-backup' ), $table );
876 $this->error( $err_msg );
877 $this->stow( "#\n# $err_msg\n#\n" );
878 }
879
880 // Comment in SQL-file
881 $this->stow( "\n\n" );
882 $this->stow( "#\n" );
883 $this->stow( '# ' . sprintf( __( 'Data contents of table %s', 'wp-db-backup' ), $this->backquote( $table ) ) . "\n" );
884 $this->stow( "#\n" );
885 }
886
887 if ( ( $segment == 'none' ) || ( $segment >= 0 ) ) {
888 $defs = array();
889 $ints = array();
890 foreach ( $table_structure as $struct ) {
891 if ( ( 0 === strpos( $struct->Type, 'tinyint' ) ) ||
892 ( 0 === strpos( strtolower( $struct->Type ), 'smallint' ) ) ||
893 ( 0 === strpos( strtolower( $struct->Type ), 'mediumint' ) ) ||
894 ( 0 === strpos( strtolower( $struct->Type ), 'int' ) ) ||
895 ( 0 === strpos( strtolower( $struct->Type ), 'bigint' ) ) ) {
896 $defs[ strtolower( $struct->Field ) ] = ( null === $struct->Default ) ? 'NULL' : $struct->Default;
897 $ints[ strtolower( $struct->Field ) ] = '1';
898 }
899 }
900
901 // Batch by $row_inc
902
903 if ( $segment == 'none' ) {
904 $row_start = 0;
905 $row_inc = DBBWP_ROWS_PER_SEGMENT;
906 } else {
907 $row_start = $segment * DBBWP_ROWS_PER_SEGMENT;
908 $row_inc = DBBWP_ROWS_PER_SEGMENT;
909 }
910
911 do {
912 // don't include extra stuff, if so requested
913 $excs = (array) get_option( 'wp_db_backup_excs' );
914 $where = '';
915
916 if ( is_array( $excs['spam'] ) && in_array( $table, $excs['spam'] ) ) {
917 $where = " WHERE comment_approved != 'spam'";
918 } elseif ( is_array( $excs['revisions'] ) && in_array( $table, $excs['revisions'] ) ) {
919 $where = " WHERE post_type != 'revision'";
920 }
921
922 if ( ! ini_get( 'safe_mode' ) ) {
923 @set_time_limit( 15 * 60 );
924 }
925 $table_data = $wpdb->get_results( "SELECT * FROM $table $where LIMIT {$row_start}, {$row_inc}", ARRAY_A );
926
927 $entries = 'INSERT INTO ' . $this->backquote( $table ) . ' VALUES (';
928 // \x08\\x09, not required
929 $search = array( "\x00", "\x0a", "\x0d", "\x1a" );
930 $replace = array( '\0', '\n', '\r', '\Z' );
931
932 if ( $table_data ) {
933 foreach ( $table_data as $row ) {
934 $values = array();
935 foreach ( $row as $key => $value ) {
936 if ( ! empty( $ints[ strtolower( $key ) ] ) ) {
937 // make sure there are no blank spots in the insert syntax,
938 // yet try to avoid quotation marks around integers
939 $value = ( null === $value || '' === $value ) ? $defs[ strtolower( $key ) ] : $value;
940 $values[] = ( '' === $value ) ? "''" : $value;
941 } elseif ( empty( $value ) ) {
942 $values[] = "'" . $value . "'";
943 } else {
944 $values[] = "'" . str_replace( $search, $replace, $this->sql_addslashes( $value ) ) . "'";
945 }
946 }
947 $this->stow( " \n" . $entries . implode( ', ', $values ) . ');' );
948 }
949 $row_start += $row_inc;
950 }
951 } while ( ( count( $table_data ) > 0 ) and ( $segment == 'none' ) );
952 }
953
954 if ( ( $segment == 'none' ) || ( $segment < 0 ) ) {
955 // Create footer/closing comment in SQL-file
956 $this->stow( "\n" );
957 $this->stow( "#\n" );
958 $this->stow( '# ' . sprintf( __( 'End of data contents of table %s', 'wp-db-backup' ), $this->backquote( $table ) ) . "\n" );
959 $this->stow( "# --------------------------------------------------------\n" );
960 $this->stow( "\n" );
961 }
962 } // end backup_table()
963
964 function db_backup( $core_tables, $other_tables ) {
965 global $table_prefix, $wpdb;
966
967 if ( wp_is_writable( $this->backup_dir ) ) {
968 $this->fp = $this->open( $this->backup_dir . $this->backup_filename );
969 if ( ! $this->fp ) {
970 $this->error( __( 'Could not open the backup file for writing!', 'wp-db-backup' ) );
971 return false;
972 }
973 } else {
974 $this->error( __( 'The backup directory is not writeable!', 'wp-db-backup' ) );
975 return false;
976 }
977
978 //Begin new backup of MySql
979 $this->stow( '# ' . __( 'WordPress MySQL database backup', 'wp-db-backup' ) . "\n" );
980 $this->stow( "#\n" );
981 $this->stow( '# ' . sprintf( __( 'Generated: %s', 'wp-db-backup' ), date( 'l j. F Y H:i T' ) ) . "\n" );
982 $this->stow( '# ' . sprintf( __( 'Hostname: %s', 'wp-db-backup' ), DB_HOST ) . "\n" );
983 $this->stow( '# ' . sprintf( __( 'Database: %s', 'wp-db-backup' ), $this->backquote( DB_NAME ) ) . "\n" );
984 $this->stow( "# --------------------------------------------------------\n" );
985
986 if ( ( is_array( $other_tables ) ) && ( count( $other_tables ) > 0 ) ) {
987 $tables = array_merge( $core_tables, $other_tables );
988 } else {
989 $tables = $core_tables;
990 }
991
992 foreach ( $tables as $table ) {
993 // Increase script execution time-limit to 15 min for every table.
994 if ( ! ini_get( 'safe_mode' ) ) {
995 @set_time_limit( 15 * 60 );
996 }
997 // Create the SQL statements
998 $this->stow( "# --------------------------------------------------------\n" );
999 $this->stow( '# ' . sprintf( __( 'Table: %s', 'wp-db-backup' ), $this->backquote( $table ) ) . "\n" );
1000 $this->stow( "# --------------------------------------------------------\n" );
1001 $this->backup_table( $table );
1002 }
1003
1004 $this->close( $this->fp );
1005
1006 if ( count( $this->errors ) ) {
1007 return false;
1008 } else {
1009 return $this->backup_filename;
1010 }
1011
1012 } //wp_db_backup
1013
1014 /**
1015 * Sends the backed-up file via email
1016 *
1017 * @param string $to
1018 * @param string $subject
1019 * @param string $message
1020 * @param string $diskfile
1021 *
1022 * @return bool
1023 */
1024 function send_mail( $to, $subject, $message, $diskfile ) {
1025 return wp_mail( $to, $subject, $message, array(), array( $diskfile ) );
1026 }
1027
1028 function deliver_backup( $filename = '', $delivery = 'http', $recipient = '', $location = 'main' ) {
1029 if ( '' == $filename ) {
1030 return false; }
1031
1032 $diskfile = $this->backup_dir . $filename;
1033 $gz_diskfile = "{$diskfile}.gz";
1034 $retry = isset( $_GET['download-retry'] );
1035 $success = false;
1036
1037 // Try to gzip the file if we can.
1038 if ( file_exists( $diskfile ) && ! file_exists( $gz_diskfile ) && ! $retry ) {
1039 if ( function_exists( 'gzencode' ) && function_exists( 'file_get_contents' ) ) {
1040 // Try upping the memory limit before gzipping
1041 if ( function_exists( 'memory_get_usage' ) && ( (int) @ini_get( 'memory_limit' ) < 64 ) ) {
1042 @ini_set( 'memory_limit', '64M' );
1043 }
1044
1045 $contents = file_get_contents( $diskfile );
1046 $gzipped = gzencode( $contents, 9 );
1047 $fp = fopen( $gz_diskfile, 'w' );
1048
1049 fwrite( $fp, $gzipped );
1050
1051 if ( fclose( $fp ) ) {
1052 unlink( $diskfile );
1053 }
1054 }
1055 }
1056
1057 if ( file_exists( $gz_diskfile ) ) {
1058 $filename = $filename . '.gz';
1059 $file_to_deliver = $gz_diskfile;
1060 } else {
1061 $file_to_deliver = $diskfile;
1062 }
1063
1064 if ( 'http' == $delivery ) {
1065 if ( ! file_exists( $file_to_deliver ) ) {
1066 if ( ! $retry ) {
1067 $this->error(
1068 array(
1069 'kind' => 'fatal',
1070 'msg' => sprintf(
1071 __(
1072 'File not found:%s',
1073 'wp-db-backup'
1074 ),
1075 "&nbsp;<strong>$filename</strong><br />"
1076 ) . '<br /><a href="' . $this->page_url . '">' . __(
1077 'Return to Backup',
1078 'wp-db-backup'
1079 ) . '</a>',
1080 )
1081 );
1082 } else {
1083 return true;
1084 }
1085 } else {
1086 header( 'Content-Description: File Transfer' );
1087 header( 'Content-Type: application/octet-stream' );
1088 header( 'Content-Length: ' . filesize( $file_to_deliver ) );
1089 header( "Content-Disposition: attachment; filename=$filename" );
1090 $success = readfile( $file_to_deliver );
1091 if ( $success ) {
1092 unlink( $file_to_deliver );
1093 }
1094 }
1095 } elseif ( 'smtp' == $delivery ) {
1096 if ( ! file_exists( $file_to_deliver ) ) {
1097 $msg = sprintf( __( 'File %s does not exist!', 'wp-db-backup' ), $file_to_deliver );
1098 $this->error( $msg );
1099 return false;
1100 }
1101
1102 if ( ! is_email( $recipient ) ) {
1103 $recipient = get_option( 'admin_email' );
1104 }
1105
1106 $message = sprintf(
1107 __( "Attached to this email is\n %1\$1s\n Size:%2\$2s kilobytes\n", 'wp-db-backup' ),
1108 $filename,
1109 round( filesize( $file_to_deliver ) / 1024 )
1110 );
1111 $blog_name = sanitize_text_field( html_entity_decode( get_bloginfo( 'name' ) ) );
1112 $success = $this->send_mail(
1113 $recipient,
1114 $blog_name . ' ' . __( 'Database Backup', 'wp-db-backup' ),
1115 $message,
1116 $file_to_deliver
1117 );
1118
1119 if ( false === $success ) {
1120 $msg = __( 'The following errors were reported:', 'wp-db-backup' ) . "\n ";
1121 if ( function_exists( 'error_get_last' ) ) {
1122 $err = error_get_last();
1123 $msg .= $err['message'];
1124 } else {
1125 $msg .= __( 'ERROR: The mail application has failed to deliver the backup.', 'wp-db-backup' );
1126 }
1127 $this->error(
1128 array(
1129 'kind' => 'fatal',
1130 'loc' => $location,
1131 'msg' => $msg,
1132 )
1133 );
1134 } else {
1135 if ( file_exists( $file_to_deliver ) ) {
1136 unlink( $file_to_deliver );
1137 }
1138 }
1139 }
1140
1141 return $success;
1142 }
1143
1144 function backup_menu() {
1145 global $table_prefix, $wpdb;
1146 $feedback = '';
1147 $whoops = false;
1148
1149 // did we just do a backup? If so, let's report the status
1150 if ( $this->backup_complete ) {
1151 $feedback = '<div class="wp-db-backup-updated"><p>' . __( 'Backup Successful', 'wp-db-backup' ) . '!';
1152 $file = $this->backup_file;
1153 switch ( $_POST['deliver'] ) {
1154 case 'http':
1155 $feedback .= '<br />' . sprintf( __( 'Your backup file: %2s should begin downloading shortly.', 'wp-db-backup' ), "{$this->backup_file}", $this->backup_file );
1156 break;
1157 case 'smtp':
1158 $email = sanitize_text_field( wp_unslash( $_POST['backup_recipient'] ) );
1159 if ( ! is_email( $email ) ) {
1160 $feedback .= get_option( 'admin_email' );
1161 } else {
1162 $feedback .= $email;
1163 }
1164 $feedback = '<br />' . sprintf( __( 'Your backup has been emailed to %s', 'wp-db-backup' ), $feedback );
1165 break;
1166 }
1167
1168 $feedback .= '</p></div>';
1169 }
1170
1171 // security check
1172 $this->wp_secure();
1173
1174 if ( count( $this->errors ) ) {
1175 $feedback .= '<div class="wp-db-backup-updated error inline"><p><strong>' . __( 'The following errors were reported:', 'wp-db-backup' ) . '</strong></p>';
1176 $feedback .= '<p>' . $this->error_display( 'main', false ) . '</p>';
1177 $feedback .= '</p></div>';
1178 }
1179
1180 // did we just save options for wp-cron?
1181 if ( ( function_exists( 'wp_schedule_event' ) || function_exists( 'wp_cron_init' ) ) && isset( $_POST['wp_cron_backup_options'] ) ) :
1182 check_admin_referer( $this->referer_check_key );
1183
1184 do_action( 'wp_db_b_update_cron_options' );
1185
1186 if ( function_exists( 'wp_schedule_event' ) ) {
1187 wp_clear_scheduled_hook( 'wp_db_backup_cron' ); // unschedule previous
1188 $scheds = (array) wp_get_schedules();
1189 $name = sanitize_text_field( strval( $_POST['wp_cron_schedule'] ) );
1190 $interval = ( isset( $scheds[ $name ]['interval'] ) ) ? (int) $scheds[ $name ]['interval'] : 0;
1191 update_option( 'wp_cron_backup_schedule', $name, false );
1192
1193 if ( 0 !== $interval ) {
1194 wp_schedule_event( time() + $interval, $name, 'wp_db_backup_cron' );
1195 }
1196 } else {
1197 update_option( 'wp_cron_backup_schedule', intval( $_POST['cron_schedule'] ), false );
1198 }
1199
1200 update_option( 'wp_cron_backup_tables', $this->get_submitted_tables_to_backup_in_cron() );
1201
1202 if ( is_email( $_POST['cron_backup_recipient'] ) ) {
1203 update_option( 'wp_cron_backup_recipient', sanitize_text_field( $_POST['cron_backup_recipient'] ), false );
1204 }
1205
1206 $feedback .= '<div class="wp-db-backup-updated wp-db-backup-schedule-updated"><p>' . __( 'Scheduled Backup Options Saved!', 'wp-db-backup' ) . '</p></div>';
1207 endif;
1208
1209 $other_tables = array();
1210 $also_backup = array();
1211
1212 // Get complete db table list
1213 $all_tables = $this->get_tables();
1214
1215 // Get list of WP tables that actually exist in this DB (for 1.6 compat!)
1216 $wp_backup_default_tables = array_intersect( $all_tables, $this->core_table_names );
1217 // Get list of non-WP tables
1218 $other_tables = array_diff( $all_tables, $wp_backup_default_tables );
1219
1220 if ( ! $this->wp_secure() ) {
1221 return;
1222 }
1223
1224 // Give the new dirs the same perms as wp-content.
1225 // $stat = stat( ABSPATH . 'wp-content' );
1226 // $dir_perms = $stat['mode'] & 0000777; // Get the permission bits.
1227 $dir_perms = '0777';
1228
1229 // the file doesn't exist and can't create it
1230 if ( ! file_exists( $this->backup_dir ) && ! @mkdir( $this->backup_dir ) ) {
1231 ?>
1232 <div class="wp-db-backup-updated error inline">
1233 <p><?php _e( 'WARNING: Your backup directory does <strong>NOT</strong> exist, and we cannot create it.', 'wp-db-backup' ); ?></p>
1234 <p><?php printf( __( 'Using your FTP client, try to create the backup directory yourself: %s', 'wp-db-backup' ), '<code>' . $this->backup_dir . '</code>' ); ?></p>
1235 </div>
1236 <?php
1237 // not writable due to write permissions
1238 $whoops = true;
1239 } elseif ( ! wp_is_writable( $this->backup_dir ) && ! @chmod( $this->backup_dir, $dir_perms ) ) {
1240 ?>
1241 <div class="wp-db-backup-updated error inline">
1242 <p><?php _e( 'WARNING: Your backup directory is <strong>NOT</strong> writable! We cannot create the backup files.', 'wp-db-backup' ); ?></p>
1243 <p><?php printf( __( 'Using your FTP client, try to set the backup directory&rsquo;s write permission to %1$s or %2$s: %3$s', 'wp-db-backup' ), '<code>777</code>', '<code>a+w</code>', '<code>' . $this->backup_dir . '</code>' ); ?></p>
1244 </div>
1245 <?php
1246 $whoops = true;
1247 } else {
1248 $this->fp = $this->open( $this->backup_dir . 'test' );
1249
1250 if ( $this->fp ) {
1251 $this->close( $this->fp );
1252 @unlink( $this->backup_dir . 'test' );
1253 // the directory is not writable probably due to safe mode
1254 } else {
1255 ?>
1256 <div class="wp-db-backup-updated error inline">
1257 <p><?php _e( 'WARNING: Your backup directory is <strong>NOT</strong> writable! We cannot create the backup files.', 'wp-db-backup' ); ?></p>
1258 <?php
1259 if ( ini_get( 'safe_mode' ) ) {
1260 ?>
1261 <p><?php _e( 'This problem seems to be caused by your server&rsquo;s <code>safe_mode</code> file ownership restrictions, which limit what files web applications like WordPress can create.', 'wp-db-backup' ); ?></p>
1262 <?php
1263 }
1264
1265 printf( __( 'You can try to correct this problem by using your FTP client to delete and then re-create the backup directory: %s', 'wp-db-backup' ), '<code>' . $this->backup_dir . '</code>' );
1266 ?>
1267 </div>
1268 <?php
1269 $whoops = true;
1270 }
1271 }
1272
1273 if ( ! file_exists( $this->backup_dir . 'index.php' ) ) {
1274 @touch( $this->backup_dir . 'index.php' );
1275 }
1276 ?>
1277 <div id="wpdb" class='wrap'>
1278 <div class="header">
1279 <img src="<?php echo plugin_dir_url( __FILE__ ) . 'assets/logo.svg'; ?>">
1280 <h2 class="title"><?php _e( 'Database Backup for WordPress', 'wp-db-backup' ); ?></h2>
1281 </div>
1282
1283 <div class="subnav">
1284 <ul>
1285 <li>
1286 <a class="active" href="#backup" data-type="backup">Backup Now</a>
1287 </li>
1288 <li>
1289 <a href="#schedule" data-type="schedule">Scheduled Backup</a>
1290 </li>
1291 </ul>
1292 </div>
1293
1294 <div class="content-wrap">
1295
1296 <?php
1297 if ( '' != $feedback ) {
1298 echo $feedback;
1299 }
1300
1301 if ( isset( $_POST['do_backup'] ) && $_POST['do_backup'] === 'fragments' ) {
1302 $this->build_backup_script();
1303 }
1304 ?>
1305
1306 <form method="post" action="">
1307 <?php
1308 if ( function_exists( 'wp_nonce_field' ) ) {
1309 wp_nonce_field( $this->referer_check_key );
1310 }
1311 ?>
1312
1313 <fieldset class="options backup-content">
1314 <legend><?php _e( 'Tables', 'wp-db-backup' ); ?></legend>
1315
1316 <div class="panel-heading">
1317 <h3>Tables</h3>
1318 </div>
1319
1320 <div class="panel-content tables">
1321
1322 <div class="tables-list core-tables alternate">
1323 <div class="instructions-container">
1324 <h4><?php _e( 'Core WordPress tables to backup', 'wp-db-backup' ); ?></h4>
1325 </div>
1326 <ul>
1327 <?php
1328 $excs = (array) get_option( 'wp_db_backup_excs' );
1329 foreach ( $wp_backup_default_tables as $table ) {
1330 if ( $table == $wpdb->comments ) {
1331 $checked = ( isset( $excs['spam'] ) && is_array( $excs['spam'] ) && in_array( $table, $excs['spam'] ) ) ? ' checked=\'checked\'' : '';
1332 echo "<li><input type='hidden' name='core_tables[]' value='$table' /><code>$table</code> <span class='instructions'><label for='exclude-spam'><input type='checkbox' id='exclude-spam' name='exclude-spam[]' value='$table' $checked /> " . __( 'Exclude spam comments', 'wp-db-backup' ) . '</label></span></li>';
1333 } elseif ( function_exists( 'wp_get_post_revisions' ) && $table == $wpdb->posts ) {
1334 $checked = ( isset( $excs['revisions'] ) && is_array( $excs['revisions'] ) && in_array( $table, $excs['revisions'] ) ) ? ' checked=\'checked\'' : '';
1335 echo "<li><input type='hidden' name='core_tables[]' value='$table' /><code>$table</code> <span class='instructions'><label for='exclude-revisions'><input type='checkbox'id='exclude-revisions' name='exclude-revisions[]' value='$table' $checked /> " . __( 'Exclude post revisions', 'wp-db-backup' ) . '</label></span></li>';
1336 } else {
1337 echo "<li><input type='hidden' name='core_tables[]' value='$table' /><code>$table</code></li>";
1338 }
1339 }
1340 ?>
1341 </ul>
1342 </div>
1343
1344 <div class="tables-list extra-tables" id="extra-tables-list">
1345
1346 <?php
1347 if ( count( $other_tables ) > 0 ) {
1348 ?>
1349 <div class="instructions-container">
1350 <h4><?php _e( 'Additional tables to backup', 'wp-db-backup' ); ?></h4>
1351 <p hidden><?php _e( 'Hold <code class="shift-key">SHIFT</code> to toggle multiple checkboxes', 'wp-db-backup' ); ?></p>
1352 </div>
1353 <ul>
1354 <?php
1355 foreach ( $other_tables as $table ) {
1356 ?>
1357 <li><label><input type="checkbox" name="other_tables[]" value="<?php echo $table; ?>" /> <code><?php echo $table; ?></code></label>
1358 <?php
1359 }
1360 ?>
1361 </ul>
1362 <?php
1363 }
1364 ?>
1365 </div>
1366
1367 </div><!--panel-content-->
1368 </fieldset>
1369
1370
1371 <!--BACKUP PANEL-->
1372 <fieldset class="options backup-content">
1373 <legend><?php _e( 'Backup Options', 'wp-db-backup' ); ?></legend>
1374
1375 <div class="panel-heading">
1376 <h3>Backup Options</h3>
1377 </div>
1378
1379 <div class="panel-content backup">
1380 <ul>
1381 <li><label for="do_download">
1382 <input type="radio" checked="checked" id="do_download" name="deliver" value="http" style="border:none;" />
1383 <?php _e( 'Download', 'wp-db-backup' ); ?>
1384 </label></li>
1385 <li><label for="do_email">
1386 <input type="radio" name="deliver" id="do_email" value="smtp" style="border:none;" />
1387 <?php
1388 $backup_recip = get_option( 'wpdb_backup_recip' );
1389 if ( empty( $backup_recip ) ) {
1390 $backup_recip = get_option( 'admin_email' );
1391 }
1392 _e( 'Send to email address', 'wp-db-backup' );
1393 ?>
1394
1395 <div class="email">
1396 <label for="backup_recipient">Email Address</label>
1397 <input type="text" id="backup_recipient" name="backup_recipient" size="20" value="<?php echo esc_attr( $backup_recip ); ?>" />
1398 </div>
1399 </label></li>
1400 </ul>
1401 <?php if ( ! $whoops ) : ?>
1402 <input type="hidden" name="do_backup" id="do_backup" value="backup" />
1403 <p class="submit">
1404 <input type="submit" name="submit" onclick="document.getElementById('do_backup').value='fragments';" value="<?php _e( 'Backup now', 'wp-db-backup' ); ?>" />
1405 </p>
1406 <?php else : ?>
1407 <div class="wp-db-backup-updated error inline"><p><?php _e( 'WARNING: Your backup directory is <strong>NOT</strong> writable!', 'wp-db-backup' ); ?></p></div>
1408 <?php endif; // ! whoops ?>
1409
1410 </div><!--panel-content-->
1411 </fieldset>
1412 <?php do_action( 'wp_db_b_backup_opts' ); ?>
1413 </form>
1414
1415 <?php
1416 // this stuff only displays if some sort of wp-cron is available
1417 $cron = ( function_exists( 'wp_schedule_event' ) ) ? true : false; // wp-cron in WP 2.1+
1418 $cron_old = ( function_exists( 'wp_cron_init' ) && ! $cron ) ? true : false; // wp-cron plugin by Skippy
1419
1420 if ( $cron_old || $cron ) :
1421 echo '<fieldset class="options schedule-content" hidden><legend>' . __( 'Scheduled Backup', 'wp-db-backup' ) . '</legend>';
1422 echo '<div class="panel-heading"><h3>Scheduled Backup</h3></div>';
1423
1424 echo '<div class="panel-content scheduled-backup">';
1425
1426 $datetime = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
1427 if ( $cron ) :
1428 $next_cron = wp_next_scheduled( 'wp_db_backup_cron' );
1429 if ( ! empty( $next_cron ) ) :
1430 ?>
1431 <p id="backup-time-wrap">
1432 <?php printf( __( '<strong>Next Backup:</strong> %s', 'wp-db-backup' ), '<span id="next-backup-time">' . gmdate( $datetime, $next_cron + ( get_option( 'gmt_offset' ) * 3600 ) ) . '</span>' ); ?>
1433 </p>
1434 <?php
1435 endif;
1436 elseif ( $cron_old ) :
1437 ?>
1438 <p><?php printf( __( 'Last WP-Cron Daily Execution: %s', 'wp-db-backup' ), gmdate( $datetime, get_option( 'wp_cron_daily_lastrun' ) + ( get_option( 'gmt_offset' ) * 3600 ) ) ); ?><br />
1439 <?php
1440 printf( __( 'Next WP-Cron Daily Execution: %s', 'wp-db-backup' ), gmdate( $datetime, ( get_option( 'wp_cron_daily_lastrun' ) + ( get_option( 'gmt_offset' ) * 3600 ) + 86400 ) ) );
1441 ?>
1442 </p>
1443 <?php
1444 endif;
1445 ?>
1446 <form method="post" action="">
1447 <?php
1448 if ( function_exists( 'wp_nonce_field' ) ) {
1449 wp_nonce_field( $this->referer_check_key );}
1450 ?>
1451
1452 <div class="panel-content row">
1453 <div class="tables-list scheduled">
1454 <h4><?php _e( 'Schedule', 'wp-db-backup' ); ?></h4>
1455 <?php
1456 if ( $cron_old ) :
1457 $wp_cron_backup_schedule = get_option( 'wp_cron_backup_schedule' );
1458 $schedule = array(
1459 0 => __( 'None', 'wp-db-backup' ),
1460 1 => __( 'Daily', 'wp-db-backup' ),
1461 );
1462 foreach ( $schedule as $value => $name ) {
1463 echo '<input type="radio" style="border:none;" name="cron_schedule"';
1464 if ( $wp_cron_backup_schedule == $value ) {
1465 echo ' checked="checked" ';
1466 }
1467 echo 'value="' . $value . '" /> ' . $name;
1468 }
1469 elseif ( $cron ) :
1470 echo apply_filters( 'wp_db_b_schedule_choices', wp_get_schedules() );
1471 endif;
1472
1473 $cron_recipient = get_option( 'wp_cron_backup_recipient' );
1474
1475 if ( ! is_email( $cron_recipient ) ) {
1476 $cron_recipient = get_option( 'admin_email' );
1477 }
1478
1479 $cron_recipient_input = '<div class="email"><label for="cron_backup_recipient">' . __( 'Email backup to', 'wp-db-backup' ) . ' <input type="text" name="cron_backup_recipient" id="cron_backup_recipient" size="20" value="' . $cron_recipient . '" /></div></label>';
1480 echo apply_filters( 'wp_db_b_cron_recipient_input', $cron_recipient_input );
1481 echo '</div>';
1482 $cron_tables = get_option( 'wp_cron_backup_tables' );
1483
1484 if ( ! is_array( $cron_tables ) ) {
1485 $cron_tables = array();
1486 }
1487
1488 if ( count( $other_tables ) > 0 ) {
1489 echo '<div class="tables-list alternate" id="include-tables-list">';
1490 echo '<div class="instructions-container">';
1491 echo '<h4>' . __( 'Tables to include in the scheduled backup:', 'wp-db-backup' ) . '</h4>';
1492 if ( count( $other_tables ) > 1 ) {
1493 echo '<p>' . __( 'Hold <code class="shift-key">SHIFT</code> to toggle multiple checkboxes', 'wp-db-backup' ) . '</p>';
1494 }
1495 echo '</div><ul>';
1496 foreach ( $other_tables as $table ) {
1497 echo '<li><label><input type="checkbox" ';
1498 if ( in_array( $table, $cron_tables ) ) {
1499 echo 'checked="checked" ';
1500 }
1501 echo "name='wp_cron_backup_tables[]' value='{$table}' /> <code>{$table}</code></label></li>";
1502 }
1503 echo '</ul></div>';
1504 echo '</div><!-- panel-content .row -->';
1505 }
1506
1507 echo '<p class="submit"><input type="submit" name="submit" value="' . __( 'Save schedule', 'wp-db-backup' ) . '" /></p>';
1508
1509 echo '<input type="hidden" name="wp_cron_backup_options" value="SET" /></form>';
1510 echo '</div><!-- .panel-content scheduled-backup -->';
1511 echo '</fieldset>';
1512 endif; // end of wp_cron (legacy) section
1513
1514 echo '</div><!-- .content-wrap -->';
1515 echo '</div><!-- .wrap -->';
1516
1517 } // end wp_backup_menu()
1518
1519 function get_sched() {
1520 $options = array_keys( (array) wp_get_schedules() );
1521 $freq = get_option( 'wp_cron_backup_schedule' );
1522 $freq = ( in_array( $freq, $options ) ) ? $freq : 'never';
1523
1524 return $freq;
1525 }
1526
1527 function schedule_choices( $schedule ) {
1528 // create the cron menu based on the schedule
1529 $wp_cron_backup_schedule = $this->get_sched();
1530 $next_cron = wp_next_scheduled( 'wp_db_backup_cron' );
1531 $wp_cron_backup_schedule = ( empty( $next_cron ) ) ? 'never' : $wp_cron_backup_schedule;
1532 $sort = array();
1533
1534 foreach ( (array) $schedule as $key => $value ) {
1535 $sort[ $key ] = $value['interval'];
1536 }
1537 asort( $sort );
1538
1539 $schedule_sorted = array();
1540 foreach ( (array) $sort as $key => $value ) {
1541 $schedule_sorted[ $key ] = $schedule[ $key ];
1542 }
1543
1544 $menu = '<ul>';
1545 $schedule = array_merge(
1546 array(
1547 'never' => array(
1548 'interval' => 0,
1549 'display' => __( 'Never', 'wp-db-backup' ),
1550 ),
1551 ),
1552 (array) $schedule_sorted
1553 );
1554
1555 foreach ( $schedule as $name => $settings ) {
1556 $interval = (int) $settings['interval'];
1557 if ( 0 == $interval && ! 'never' == $name ) {
1558 continue;
1559 }
1560 $display = ( ! '' == $settings['display'] ) ? $settings['display'] : sprintf( __( '%s seconds', 'wp-db-backup' ), $interval );
1561 $menu .= "<li><label for='$name'><input type='radio' name='wp_cron_schedule' style='border:none;' ";
1562 if ( $wp_cron_backup_schedule == $name ) {
1563 $menu .= " checked='checked' ";
1564 }
1565 $menu .= "id='$name' value='$name' />$display</label></li>";
1566 }
1567
1568 $menu .= '</ul>';
1569
1570 return $menu;
1571 } // end schedule_choices()
1572
1573 function wp_cron_daily() {
1574 // for legacy cron plugin
1575 $schedule = intval( get_option( 'wp_cron_backup_schedule' ) );
1576
1577 // If scheduled backup is disabled
1578 if ( 0 == $schedule ) {
1579 return;
1580 } else {
1581 return $this->cron_backup();
1582 }
1583 }
1584
1585 function cron_backup() {
1586 global $table_prefix, $wpdb;
1587
1588 $all_tables = $this->get_tables();
1589 $core_tables = array_intersect( $all_tables, $this->core_table_names );
1590 $other_tables = get_option( 'wp_cron_backup_tables' );
1591 $recipient = get_option( 'wp_cron_backup_recipient' );
1592 $backup_file = $this->db_backup( $core_tables, $other_tables );
1593
1594 if ( false !== $backup_file ) {
1595 return $this->deliver_backup( $backup_file, 'smtp', $recipient, 'main' );
1596 } else {
1597 return false;
1598 }
1599 }
1600
1601 function add_sched_options( $sched ) {
1602 $sched['weekly'] = array(
1603 'interval' => 604800,
1604 'display' => __( 'Once Weekly', 'wp-db-backup' ),
1605 );
1606
1607 return $sched;
1608 }
1609
1610 /**
1611 * Checks that WordPress has sufficient security measures
1612 * @param string $kind
1613 * @return bool
1614 */
1615 function wp_secure( $kind = 'warn', $loc = 'main' ) {
1616 global $wp_version;
1617
1618 if ( function_exists( 'wp_verify_nonce' ) ) {
1619 return true;
1620 } else {
1621 $this->error(
1622 array(
1623 'kind' => $kind,
1624 'loc' => $loc,
1625 'msg' => sprintf(
1626 __(
1627 'Your WordPress version, %1$1s, lacks important security features without which it is unsafe to use the WP-DB-Backup plugin. Hence, this plugin is automatically disabled. Please consider <a href="%2$2s">upgrading WordPress</a> to a more recent version.',
1628 'wp-db-backup'
1629 ),
1630 $wp_version,
1631 'http://wordpress.org/download/'
1632 ),
1633 )
1634 );
1635
1636 return false;
1637 }
1638 }
1639
1640 /**
1641 * Checks that the user has sufficient permission to backup
1642 * @param string $loc
1643 * @return bool
1644 */
1645 function can_user_backup( $loc = 'main' ) {
1646 $can = false;
1647
1648 // make sure WPMU users are site admins, not ordinary admins
1649 if ( function_exists( 'is_site_admin' ) && ! is_site_admin() ) {
1650 $this->error(
1651 array(
1652 'loc' => $loc,
1653 'kind' => 'fatal',
1654 'msg' => __(
1655 'You are not allowed to perform backups.',
1656 'wp-db-backup'
1657 ),
1658 )
1659 );
1660 return false;
1661 }
1662
1663 if ( ( $this->wp_secure( 'fatal', $loc ) ) && current_user_can( 'import' ) ) {
1664 $can = $this->verify_nonce( $_REQUEST['_wpnonce'], $this->referer_check_key, $loc );
1665 }
1666
1667 if ( false == $can ) {
1668 $this->error(
1669 array(
1670 'loc' => $loc,
1671 'kind' => 'fatal',
1672 'msg' => __(
1673 'You are not allowed to perform backups.',
1674 'wp-db-backup'
1675 ),
1676 )
1677 );
1678 }
1679
1680 return $can;
1681 }
1682
1683 /**
1684 * Verify that the nonce is legitimate
1685 * @param string $rec the nonce received
1686 * @param string $nonce what the nonce should be
1687 * @param string $loc the location of the check
1688 * @return bool
1689 */
1690 function verify_nonce( $rec = '', $nonce = 'X', $loc = 'main' ) {
1691 if ( wp_verify_nonce( $rec, $nonce ) ) {
1692 return true;
1693 } else {
1694 $this->error(
1695 array(
1696 'loc' => $loc,
1697 'kind' => 'fatal',
1698 'msg' => sprintf(
1699 __(
1700 'There appears to be an unauthorized attempt from this site to access your database located at %1s. The attempt has been halted.',
1701 'wp-db-backup'
1702 ),
1703 get_option( 'home' )
1704 ),
1705 )
1706 );
1707 }
1708 }
1709
1710 /**
1711 * Check whether a file to be downloaded is
1712 * surreptitiously trying to download a non-backup file
1713 * @param string $file
1714 * @return null
1715 */
1716 function validate_file( $file ) {
1717 if ( ( false !== strpos( $file, '..' ) ) || ( false !== strpos( $file, './' ) ) || ( ':' == substr( $file, 1, 1 ) ) ) {
1718 $this->error(
1719 array(
1720 'kind' => 'fatal',
1721 'loc' => 'frame',
1722 'msg' => __(
1723 "Cheatin' uh ?",
1724 'wp-db-backup'
1725 ),
1726 )
1727 );
1728 }
1729 }
1730
1731 /**
1732 * Get the sitename by query $_SERVER['SERVER_NAME'].
1733 * If it is not set, then use site_url() instead
1734 * @return string
1735 */
1736 function get_sitename() {
1737 $sitename = '';
1738
1739 if ( isset( $_SERVER['SERVER_NAME'] ) ) {
1740 $sitename = strtolower( sanitize_text_field( $_SERVER['SERVER_NAME'] ) );
1741 } else {
1742 if ( function_exists( 'site_url' ) ) {
1743 // site_url() was added since 3.0.0
1744 // force http scheme so we can easily get rid of leading http://
1745 $sitename = strtolower( site_url( '', 'http' ) );
1746 $sitename = substr( $sitename, 7 );
1747 } else {
1748 // try to be compatible with versions < 3.0.0
1749 $sitename = strtolower( get_option( 'siteurl' ) );
1750 if ( substr( $sitename, 0, 7 ) == 'http://' ) {
1751 $sitename = substr( $sitename, 7 );
1752 } elseif ( substr( $sitename, 0, 8 ) == 'https://' ) {
1753 $sitename = substr( $sitename, 8 );
1754 }
1755 }
1756 }
1757
1758 // get rid of www
1759 if ( substr( $sitename, 0, 4 ) == 'www.' ) {
1760 $sitename = substr( $sitename, 4 );
1761 }
1762
1763 return $sitename;
1764 }
1765
1766
1767 /**
1768 * Sanitize an array of content.
1769 *
1770 * @param array $array_of_data
1771 *
1772 * @return array
1773 */
1774 function sanitize_array( $array_to_sanitize ) {
1775 $sanitized = array();
1776
1777 foreach ( $array_to_sanitize as $key => $value ) {
1778 $sanitized[ $key ] = sanitize_text_field( $value );
1779 }
1780
1781 return $sanitized;
1782 }
1783
1784 /**
1785 * Get a sanitized array of submitted $_POST values
1786 *
1787 * @param string $post_key The key of the $_POST array.
1788 *
1789 * @return array
1790 */
1791 function get_post_data_array( $post_key ) {
1792 $sanitized_data = array();
1793
1794 if ( isset( $_POST[ $post_key ] ) ) {
1795 $sanitized_data = (array) $_POST[ $post_key ];
1796 }
1797
1798 return $this->sanitize_array( $sanitized_data );
1799 }
1800
1801 /**
1802 * Get the revisions to exclude.
1803 *
1804 * @return array
1805 */
1806 function get_revisions_to_exclude() {
1807 return $this->get_post_data_array( 'exclude-revisions' );
1808 }
1809
1810 /**
1811 * Get the spam to exclude.
1812 *
1813 * @return array
1814 */
1815 function get_spam_to_exclude() {
1816 return $this->get_post_data_array( 'exclude-spam' );
1817 }
1818
1819 /**
1820 * Get the submitted tables to backup.
1821 *
1822 * @return array
1823 */
1824 function get_submitted_tables_to_backup_in_cron() {
1825 return $this->get_post_data_array( 'wp_cron_backup_tables' );
1826 }
1827
1828 /**
1829 * Get an array of all tables on the current WP install.
1830 *
1831 * @return array
1832 */
1833 function get_tables() {
1834 global $wpdb;
1835
1836 $all_tables = $wpdb->get_results( 'SHOW TABLES', ARRAY_N );
1837
1838 return array_map(
1839 function( $a ) {
1840 return $a[0];
1841 },
1842 $all_tables
1843 );
1844 }
1845
1846 }
1847
1848 function wpdbBackup_init() {
1849 global $mywpdbbackup;
1850 $mywpdbbackup = new wpdbBackup();
1851 }
1852
1853 add_action( 'plugins_loaded', 'wpdbBackup_init' );
1854