PluginProbe
Database Backup for WordPress / 2.5
Database Backup for WordPress v2.5
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 2.5, at wp-db-backup.php

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