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

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