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

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