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

964 lines 33.5 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: http://www.ilfilosofo.com/blog/wp-db-backup
5 Description: On-demand backup of your WordPress database. Navigate to <a href="edit.php?page=wp-db-backup.php">Manage &rarr; Backup</a> to get started.
6 Author: <a href="http://www.skippy.net/">Scott Merrill</a> and <a href="http://www.ilfilosofo.com/blog/">Austin Matzko</a>
7 Version: 2.0
8
9 Much of this was modified from Mark Ghosh's One Click Backup, which
10 in turn was derived from phpMyAdmin.
11
12 Many thanks to Owen (http://asymptomatic.net/wp/) for his patch
13 http://dev.wp-plugins.org/ticket/219
14 */
15
16 // CHANGE THIS IF YOU WANT TO USE A
17 // DIFFERENT BACKUP LOCATION
18
19 $rand = substr( md5( md5( DB_PASSWORD ) ), -5 );
20
21 define('WP_BACKUP_DIR', 'wp-content/backup-' . $rand);
22
23 define('ROWS_PER_SEGMENT', 100);
24
25 class wpdbBackup {
26
27 var $backup_complete = false;
28 var $backup_file = '';
29 var $backup_dir = WP_BACKUP_DIR;
30 var $backup_errors = array();
31 var $basename;
32
33 function gzip() {
34 return function_exists('gzopen');
35 }
36
37 function wpdbBackup() {
38 global $table_prefix, $wpdb;
39 add_action('wp_db_backup_cron', array(&$this, 'cron_backup'));
40 add_action('wp_cron_daily', array(&$this, 'wp_cron_daily'));
41 add_filter('cron_schedules', array(&$this, 'add_sched_options'));
42 add_filter('wp_db_b_schedule_choices', array(&$this, 'schedule_choices'));
43
44 $table_prefix = ( isset( $table_prefix ) ) ? $table_prefix : $wpdb->prefix;
45 if ( isset( $wpdb->link2cat ) )
46 $this->core_table_names = explode(',',"$wpdb->categories,$wpdb->comments,$wpdb->link2cat,$wpdb->links,$wpdb->options,$wpdb->post2cat,$wpdb->postmeta,$wpdb->posts,$wpdb->users,$wpdb->usermeta");
47 else
48 $this->core_table_names = explode(',',"$wpdb->categories,$wpdb->comments,$wpdb->linkcategories,$wpdb->links,$wpdb->options,$wpdb->post2cat,$wpdb->postmeta,$wpdb->posts,$wpdb->users,$wpdb->usermeta");
49
50 $this->backup_dir = trailingslashit($this->backup_dir);
51 $this->basename = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', __FILE__);
52
53 if (isset($_POST['do_backup'])) {
54 if ( !$this->user_can_backup() ) die(__('You are not allowed to perform backups.'));
55 switch($_POST['do_backup']) {
56 case 'backup':
57 $this->perform_backup();
58 break;
59 case 'fragments':
60 add_action('admin_menu', array(&$this, 'fragment_menu'));
61 break;
62 }
63 } elseif (isset($_GET['fragment'] )) {
64 if ( !$this->user_can_backup() ) die(__('You are not allowed to perform backups.'));
65 add_action('init', array(&$this, 'init'));
66 } elseif (isset($_GET['backup'] )) {
67 if ( !$this->user_can_backup() ) die(__('You are not allowed to perform backups.'));
68 add_action('init', array(&$this, 'init'));
69 } else {
70 add_action('admin_menu', array(&$this, 'admin_menu'));
71 }
72 }
73
74 function init() {
75 if ( !$this->user_can_backup() ) die(__('You are not allowed to perform backups.'));
76
77 if (isset($_GET['backup'])) {
78 $via = isset($_GET['via']) ? $_GET['via'] : 'http';
79
80 $this->backup_file = $_GET['backup'];
81 $this->validate_file($this->backup_file);
82
83 switch($via) {
84 case 'smtp':
85 case 'email':
86 $this->deliver_backup ($this->backup_file, 'smtp', $_GET['recipient']);
87 echo '
88 <!-- ' . $via . ' -->
89 <script type="text/javascript"><!--\\
90 ';
91 if($this->backup_errors) {
92 foreach($this->backup_errors as $error) {
93 echo "window.parent.addError('$error');\n";
94 }
95 }
96 echo '
97 alert("' . __('Backup Complete!') . '");
98 </script>
99 ';
100 break;
101 default:
102 $this->deliver_backup ($this->backup_file, $via);
103 }
104 die();
105 }
106 if (isset($_GET['fragment'] )) {
107 list($table, $segment, $filename) = explode(':', $_GET['fragment']);
108 $this->validate_file($filename);
109 $this->backup_fragment($table, $segment, $filename);
110 }
111
112 die();
113 }
114
115 function build_backup_script() {
116 global $table_prefix, $wpdb;
117
118 $datum = date("Ymd_B");
119 $backup_filename = DB_NAME . "_$table_prefix$datum.sql";
120 if ($this->gzip()) $backup_filename .= '.gz';
121
122 echo "<div class='wrap'>";
123 //echo "<pre>" . print_r($_POST, 1) . "</pre>";
124 echo '<h2>' . __('Backup') . '</h2>
125 <fieldset class="options"><legend>' . __('Progress') . '</legend>
126 <p><strong>' .
127 __('DO NOT DO THE FOLLOWING AS IT WILL CAUSE YOUR BACKUP TO FAIL:').
128 '</strong></p>
129 <ol>
130 <li>'.__('Close this browser').'</li>
131 <li>'.__('Reload this page').'</li>
132 <li>'.__('Click the Stop or Back buttons in your browser').'</li>
133 </ol>
134 <p><strong>' . __('Progress:') . '</strong></p>
135 <div id="meterbox" style="height:11px;width:80%;padding:3px;border:1px solid #659fff;"><div id="meter" style="height:11px;background-color:#659fff;width:0%;text-align:center;font-size:6pt;">&nbsp;</div></div>
136 <div id="progress_message"></div>
137 <div id="errors"></div>
138 </fieldset>
139 <iframe id="backuploader" src="about:blank" style="border:0px solid white;height:1em;width:1em;"></iframe>
140 <script type="text/javascript"><!--//
141 function setMeter(pct) {
142 var meter = document.getElementById("meter");
143 meter.style.width = pct + "%";
144 meter.innerHTML = Math.floor(pct) + "%";
145 }
146 function setProgress(str) {
147 var progress = document.getElementById("progress_message");
148 progress.innerHTML = str;
149 }
150 function addError(str) {
151 var errors = document.getElementById("errors");
152 errors.innerHTML = errors.innerHTML + str + "<br />";
153 }
154
155 function backup(table, segment) {
156 var fram = document.getElementById("backuploader");
157 fram.src = "' . $_SERVER['REQUEST_URI'] . '&fragment=" + table + ":" + segment + ":' . $backup_filename . '";
158 }
159
160 var curStep = 0;
161
162 function nextStep() {
163 backupStep(curStep);
164 curStep++;
165 }
166
167 function finishBackup() {
168 var fram = document.getElementById("backuploader");
169 setMeter(100);
170 ';
171
172 $this_basename = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', __FILE__);
173 $download_uri = get_option('siteurl') . "/wp-admin/edit.php?page={$this_basename}&backup={$backup_filename}";
174 switch($_POST['deliver']) {
175 case 'http':
176 echo '
177 setProgress("' . sprintf(__("Backup complete, preparing <a href=\\\"%s\\\">backup</a> for download..."), $download_uri) . '");
178 fram.src = "' . $download_uri . '";
179 ';
180 break;
181 case 'smtp':
182 echo '
183 setProgress("' . sprintf(__("Backup complete, sending <a href=\\\"%s\\\">backup</a> via email..."), $download_uri) . '");
184 fram.src = "' . $download_uri . '&via=email&recipient=' . $_POST['backup_recipient'] . '";
185 ';
186 break;
187 default:
188 echo '
189 setProgress("' . sprintf(__("Backup complete, download <a href=\\\"%s\\\">here</a>."), $download_uri) . '");
190 ';
191 }
192
193 echo '
194 }
195
196 function backupStep(step) {
197 switch(step) {
198 case 0: backup("", 0); break;
199 ';
200
201 $also_backup = array();
202 if (isset($_POST['other_tables'])) {
203 $also_backup = $_POST['other_tables'];
204 } else {
205 $also_backup = array();
206 }
207 $core_tables = $_POST['core_tables'];
208 $tables = array_merge($core_tables, $also_backup);
209 $step_count = 1;
210 foreach ($tables as $table) {
211 $rec_count = $wpdb->get_var("SELECT count(*) FROM {$table}");
212 $rec_segments = ceil($rec_count / ROWS_PER_SEGMENT);
213 $table_count = 0;
214 do {
215 echo "case {$step_count}: backup(\"{$table}\", {$table_count}); break;\n";
216 $step_count++;
217 $table_count++;
218 } while($table_count < $rec_segments);
219 echo "case {$step_count}: backup(\"{$table}\", -1); break;\n";
220 $step_count++;
221 }
222 echo "case {$step_count}: finishBackup(); break;";
223
224 echo '
225 }
226 if(step != 0) setMeter(100 * step / ' . $step_count . ');
227 }
228
229 nextStep();
230 //--></script>
231 </div>
232 ';
233 }
234
235 function backup_fragment($table, $segment, $filename) {
236 global $table_prefix, $wpdb;
237
238 echo "$table:$segment:$filename";
239
240 if($table == '') {
241 $msg = __('Creating backup file...');
242 } else {
243 if($segment == -1) {
244 $msg = sprintf(__('Finished backing up table \\"%s\\".'), $table);
245 } else {
246 $msg = sprintf(__('Backing up table \\"%s\\"...'), $table);
247 }
248 }
249
250 echo '<script type="text/javascript"><!--//
251 var msg = "' . $msg . '";
252 window.parent.setProgress(msg);
253 ';
254
255 if (is_writable(ABSPATH . $this->backup_dir)) {
256 $this->fp = $this->open(ABSPATH . $this->backup_dir . $filename, 'a');
257 if(!$this->fp) {
258 $this->backup_error(__('Could not open the backup file for writing!'));
259 $this->fatal_error = __('The backup file could not be saved. Please check the permissions for writing to your backup directory and try again.');
260 }
261 else {
262 if($table == '') {
263 //Begin new backup of MySql
264 $this->stow("# WordPress MySQL database backup\n");
265 $this->stow("#\n");
266 $this->stow("# Generated: " . date("l j. F Y H:i T") . "\n");
267 $this->stow("# Hostname: " . DB_HOST . "\n");
268 $this->stow("# Database: " . $this->backquote(DB_NAME) . "\n");
269 $this->stow("# --------------------------------------------------------\n");
270 } else {
271 if($segment == 0) {
272 // Increase script execution time-limit to 15 min for every table.
273 if ( !ini_get('safe_mode')) @set_time_limit(15*60);
274 //ini_set('memory_limit', '16M');
275 // Create the SQL statements
276 $this->stow("# --------------------------------------------------------\n");
277 $this->stow("# Table: " . $this->backquote($table) . "\n");
278 $this->stow("# --------------------------------------------------------\n");
279 }
280 $this->backup_table($table, $segment);
281 }
282 }
283 } else {
284 $this->backup_error(__('The backup directory is not writeable!'));
285 $this->fatal_error = __('The backup directory is not writeable! Please check the permissions for writing to your backup directory and try again.');
286 }
287
288 if($this->fp) $this->close($this->fp);
289
290 if($this->backup_errors) {
291 foreach($this->backup_errors as $error) {
292 echo "window.parent.addError('$error');\n";
293 }
294 }
295 if($this->fatal_error) {
296 echo '
297 alert("' . addslashes($this->fatal_error) . '");
298 //--></script>
299 ';
300 }
301 else {
302 echo '
303 window.parent.nextStep();
304 //--></script>
305 ';
306 }
307
308 die();
309 }
310
311 function perform_backup() {
312 // are we backing up any other tables?
313 $also_backup = array();
314 if (isset($_POST['other_tables'])) {
315 $also_backup = $_POST['other_tables'];
316 }
317
318 $core_tables = $_POST['core_tables'];
319 $this->backup_file = $this->db_backup($core_tables, $also_backup);
320 if (FALSE !== $this->backup_file) {
321 if ('smtp' == $_POST['deliver']) {
322 $this->deliver_backup ($this->backup_file, $_POST['deliver'], $_POST['backup_recipient']);
323 } elseif ('http' == $_POST['deliver']) {
324 $this_basename = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', __FILE__);
325 header('Refresh: 3; ' . get_option('siteurl') . "/wp-admin/edit.php?page={$this_basename}&backup={$this->backup_file}");
326 }
327 // we do this to say we're done.
328 $this->backup_complete = true;
329 }
330 }
331
332 function admin_menu() {
333 add_management_page(__('Backup'), __('Backup'), 'import', basename(__FILE__), array(&$this, 'backup_menu'));
334 }
335
336 function fragment_menu() {
337 add_management_page(__('Backup'), __('Backup'), 'import', basename(__FILE__), array(&$this, 'build_backup_script'));
338 }
339
340 function sql_addslashes($a_string = '', $is_like = FALSE) {
341 /*
342 Better addslashes for SQL queries.
343 Taken from phpMyAdmin.
344 */
345 if ($is_like) {
346 $a_string = str_replace('\\', '\\\\\\\\', $a_string);
347 } else {
348 $a_string = str_replace('\\', '\\\\', $a_string);
349 }
350 $a_string = str_replace('\'', '\\\'', $a_string);
351
352 return $a_string;
353 } // function sql_addslashes($a_string = '', $is_like = FALSE)
354
355 function backquote($a_name) {
356 /*
357 Add backqouotes to tables and db-names in
358 SQL queries. Taken from phpMyAdmin.
359 */
360 if (!empty($a_name) && $a_name != '*') {
361 if (is_array($a_name)) {
362 $result = array();
363 reset($a_name);
364 while(list($key, $val) = each($a_name)) {
365 $result[$key] = '`' . $val . '`';
366 }
367 return $result;
368 } else {
369 return '`' . $a_name . '`';
370 }
371 } else {
372 return $a_name;
373 }
374 } // function backquote($a_name, $do_it = TRUE)
375
376 function open($filename = '', $mode = 'w') {
377 if ('' == $filename) return false;
378 if ($this->gzip()) {
379 $fp = @gzopen($filename, $mode);
380 } else {
381 $fp = @fopen($filename, $mode);
382 }
383 return $fp;
384 }
385
386 function close($fp) {
387 if ($this->gzip()) {
388 gzclose($fp);
389 } else {
390 fclose($fp);
391 }
392 }
393
394 function stow($query_line) {
395 if ($this->gzip()) {
396 if(@gzwrite($this->fp, $query_line) === FALSE) {
397 backup_error(__('There was an error writing a line to the backup script:'));
398 backup_error('&nbsp;&nbsp;' . $query_line);
399 }
400 } else {
401 if(@fwrite($this->fp, $query_line) === FALSE) {
402 backup_error(__('There was an error writing a line to the backup script:'));
403 backup_error('&nbsp;&nbsp;' . $query_line);
404 }
405 }
406 }
407
408 function backup_error($err) {
409 if(count($this->backup_errors) < 20) {
410 $this->backup_errors[] = $err;
411 } elseif(count($this->backup_errors) == 20) {
412 $this->backup_errors[] = __('Subsequent errors have been omitted from this log.');
413 }
414 }
415
416 function backup_table($table, $segment = 'none') {
417 global $wpdb;
418
419 /*
420 Taken partially from phpMyAdmin and partially from
421 Alain Wolf, Zurich - Switzerland
422 Website: http://restkultur.ch/personal/wolf/scripts/db_backup/
423
424 Modified by Scott Merril (http://www.skippy.net/)
425 to use the WordPress $wpdb object
426 */
427
428 $table_structure = $wpdb->get_results("DESCRIBE $table");
429 if (! $table_structure) {
430 backup_errors(__('Error getting table details') . ": $table");
431 return FALSE;
432 }
433
434 if(($segment == 'none') || ($segment == 0)) {
435 // Add SQL statement to drop existing table
436 $this->stow("\n\n");
437 $this->stow("#\n");
438 $this->stow("# Delete any existing table " . $this->backquote($table) . "\n");
439 $this->stow("#\n");
440 $this->stow("\n");
441 $this->stow("DROP TABLE IF EXISTS " . $this->backquote($table) . ";\n");
442
443 // Table structure
444 // Comment in SQL-file
445 $this->stow("\n\n");
446 $this->stow("#\n");
447 $this->stow("# Table structure of table " . $this->backquote($table) . "\n");
448 $this->stow("#\n");
449 $this->stow("\n");
450
451 $create_table = $wpdb->get_results("SHOW CREATE TABLE $table", ARRAY_N);
452 if (FALSE === $create_table) {
453 $this->backup_error(sprintf(__("Error with SHOW CREATE TABLE for %s."), $table));
454 $this->stow("#\n# Error with SHOW CREATE TABLE for $table!\n#\n");
455 }
456 $this->stow($create_table[0][1] . ' ;');
457
458 if (FALSE === $table_structure) {
459 $this->backup_error(sprintf(__("Error getting table structure of %s"), $table));
460 $this->stow("#\n# Error getting table structure of $table!\n#\n");
461 }
462
463 // Comment in SQL-file
464 $this->stow("\n\n");
465 $this->stow("#\n");
466 $this->stow('# Data contents of table ' . $this->backquote($table) . "\n");
467 $this->stow("#\n");
468 }
469
470 if(($segment == 'none') || ($segment >= 0)) {
471 $ints = array();
472 foreach ($table_structure as $struct) {
473 if ( (0 === strpos($struct->Type, 'tinyint')) ||
474 (0 === strpos(strtolower($struct->Type), 'smallint')) ||
475 (0 === strpos(strtolower($struct->Type), 'mediumint')) ||
476 (0 === strpos(strtolower($struct->Type), 'int')) ||
477 (0 === strpos(strtolower($struct->Type), 'bigint')) ||
478 (0 === strpos(strtolower($struct->Type), 'timestamp')) ) {
479 $ints[strtolower($struct->Field)] = "1";
480 }
481 }
482
483
484 // Batch by $row_inc
485
486 if($segment == 'none') {
487 $row_start = 0;
488 $row_inc = ROWS_PER_SEGMENT;
489 } else {
490 $row_start = $segment * ROWS_PER_SEGMENT;
491 $row_inc = ROWS_PER_SEGMENT;
492 }
493
494 do {
495 if ( !ini_get('safe_mode')) @set_time_limit(15*60);
496 $table_data = $wpdb->get_results("SELECT * FROM $table LIMIT {$row_start}, {$row_inc}", ARRAY_A);
497
498 /*
499 if (FALSE === $table_data) {
500 $wp_backup_error .= "Error getting table contents from $table\r\n";
501 fwrite($fp, "#\n# Error getting table contents fom $table!\n#\n");
502 }
503 */
504
505 $entries = 'INSERT INTO ' . $this->backquote($table) . ' VALUES (';
506 // \x08\\x09, not required
507 $search = array("\x00", "\x0a", "\x0d", "\x1a");
508 $replace = array('\0', '\n', '\r', '\Z');
509 if($table_data) {
510 foreach ($table_data as $row) {
511 $values = array();
512 foreach ($row as $key => $value) {
513 if ($ints[strtolower($key)]) {
514 $values[] = $value;
515 } else {
516 $values[] = "'" . str_replace($search, $replace, $this->sql_addslashes($value)) . "'";
517 }
518 }
519 $this->stow(" \n" . $entries . implode(', ', $values) . ') ;');
520 }
521 $row_start += $row_inc;
522 }
523 } while((count($table_data) > 0) and ($segment=='none'));
524 }
525
526
527 if(($segment == 'none') || ($segment < 0)) {
528 // Create footer/closing comment in SQL-file
529 $this->stow("\n");
530 $this->stow("#\n");
531 $this->stow("# End of data contents of table " . $this->backquote($table) . "\n");
532 $this->stow("# --------------------------------------------------------\n");
533 $this->stow("\n");
534 }
535
536 } // end backup_table()
537
538 function return_bytes($val) {
539 $val = trim($val);
540 $last = strtolower($val{strlen($val)-1});
541 switch($last) {
542 // The 'G' modifier is available since PHP 5.1.0
543 case 'g':
544 $val *= 1024;
545 case 'm':
546 $val *= 1024;
547 case 'k':
548 $val *= 1024;
549 }
550
551 return $val;
552 }
553
554 function db_backup($core_tables, $other_tables) {
555 global $table_prefix, $wpdb;
556
557 $datum = date("Ymd_B");
558 $wp_backup_filename = DB_NAME . "_$table_prefix$datum.sql";
559 if ($this->gzip()) {
560 $wp_backup_filename .= '.gz';
561 }
562
563 if (is_writable(ABSPATH . $this->backup_dir)) {
564 $this->fp = $this->open(ABSPATH . $this->backup_dir . $wp_backup_filename);
565 if(!$this->fp) {
566 $this->backup_error(__('Could not open the backup file for writing!'));
567 return false;
568 }
569 } else {
570 $this->backup_error(__('The backup directory is not writeable!'));
571 return false;
572 }
573
574 //Begin new backup of MySql
575 $this->stow("# WordPress MySQL database backup\n");
576 $this->stow("#\n");
577 $this->stow("# Generated: " . date("l j. F Y H:i T") . "\n");
578 $this->stow("# Hostname: " . DB_HOST . "\n");
579 $this->stow("# Database: " . $this->backquote(DB_NAME) . "\n");
580 $this->stow("# --------------------------------------------------------\n");
581
582 if ( (is_array($other_tables)) && (count($other_tables) > 0) )
583 $tables = array_merge($core_tables, $other_tables);
584 else
585 $tables = $core_tables;
586
587 foreach ($tables as $table) {
588 // Increase script execution time-limit to 15 min for every table.
589 if ( !ini_get('safe_mode')) @set_time_limit(15*60);
590 // Create the SQL statements
591 $this->stow("# --------------------------------------------------------\n");
592 $this->stow("# Table: " . $this->backquote($table) . "\n");
593 $this->stow("# --------------------------------------------------------\n");
594 $this->backup_table($table);
595 }
596
597 $this->close($this->fp);
598
599 if (count($this->backup_errors)) {
600 return false;
601 } else {
602 return $wp_backup_filename;
603 }
604
605 } //wp_db_backup
606
607 function deliver_backup ($filename = '', $delivery = 'http', $recipient = '') {
608 if ('' == $filename) { return FALSE; }
609
610 $diskfile = ABSPATH . $this->backup_dir . $filename;
611 if ('http' == $delivery) {
612 if (! file_exists($diskfile)) {
613 $msg = sprintf(__('File not found:%s'), "<br /><strong>$filename</strong><br />");
614 $this_basename = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', __FILE__);
615 $msg .= '<br /><a href="' . get_option('siteurl') . "/wp-admin/edit.php?page={$this_basename}" . '">' . __('Return to Backup');
616 die($msg);
617 }
618 header('Content-Description: File Transfer');
619 header('Content-Type: application/octet-stream');
620 header('Content-Length: ' . filesize($diskfile));
621 header("Content-Disposition: attachment; filename=$filename");
622 readfile($diskfile);
623 unlink($diskfile);
624 } elseif ('smtp' == $delivery) {
625 if (! file_exists($diskfile)) return false;
626
627 if (! is_email ($recipient)) {
628 $recipient = get_option('admin_email');
629 }
630 $randomish = md5(time());
631 $boundary = "==WPBACKUP-BY-SKIPPY-$randomish";
632 $fp = fopen($diskfile,"rb");
633 $file = fread($fp,filesize($diskfile));
634 $this->close($fp);
635 $data = chunk_split(base64_encode($file));
636 $headers = "MIME-Version: 1.0\n";
637 $headers .= "Content-Type: multipart/mixed; boundary=\"$boundary\"\n";
638 $headers .= 'From: ' . get_option('admin_email') . "\n";
639
640 $message = sprintf(__("Attached to this email is\n %1s\n Size:%2s kilobytes\n"), $filename, round(filesize($diskfile)/1024));
641 // Add a multipart boundary above the plain message
642 $message = "This is a multi-part message in MIME format.\n\n" .
643 "--{$boundary}\n" .
644 "Content-Type: text/plain; charset=\"utf-8\"\n" .
645 "Content-Transfer-Encoding: 7bit\n\n" .
646 $message . "\n\n";
647
648 // Add file attachment to the message
649 $message .= "--{$boundary}\n" .
650 "Content-Type: application/octet-stream;\n" .
651 " name=\"{$filename}\"\n" .
652 "Content-Disposition: attachment;\n" .
653 " filename=\"{$filename}\"\n" .
654 "Content-Transfer-Encoding: base64\n\n" .
655 $data . "\n\n" .
656 "--{$boundary}--\n";
657
658 if (function_exists('wp_mail')) {
659 wp_mail ($recipient, get_bloginfo('name') . ' ' . __('Database Backup'), $message, $headers);
660 } else {
661 mail ($recipient, get_bloginfo('name') . ' ' . __('Database Backup'), $message, $headers);
662 }
663
664 unlink($diskfile);
665 }
666 return;
667 }
668
669 function backup_menu() {
670 global $table_prefix, $wpdb;
671 $feedback = '';
672 $WHOOPS = FALSE;
673
674 // did we just do a backup? If so, let's report the status
675 if ( $this->backup_complete ) {
676 $feedback = '<div class="updated"><p>' . __('Backup Successful') . '!';
677 $file = $this->backup_file;
678 switch($_POST['deliver']) {
679 case 'http':
680 $feedback .= '<br />' . sprintf(__('Your backup file: <a href="%1s">%2s</a> should begin downloading shortly.'), get_option('siteurl') . "/{$this->backup_dir}{$this->backup_file}", $this->backup_file);
681 break;
682 case 'smtp':
683 if (! is_email($_POST['backup_recipient'])) {
684 $feedback .= get_option('admin_email');
685 } else {
686 $feedback .= $_POST['backup_recipient'];
687 }
688 $feedback = '<br />' . sprintf(__('Your backup has been emailed to %s'), $feedback);
689 break;
690 case 'none':
691 $feedback .= '<br />' . __('Your backup file has been saved on the server. If you would like to download it now, right click and select "Save As"');
692 $feedback .= ':<br /> <a href="' . get_option('siteurl') . "/{$this->backup_dir}$file\">$file</a> : " . sprintf(__('%s bytes'), filesize(ABSPATH . $this->backup_dir . $file));
693 }
694 $feedback .= '</p></div>';
695 }
696
697 if (count($this->backup_errors)) {
698 $feedback .= '<div class="updated error">' . __('The following errors were reported:') . "<pre>";
699 foreach($this->backup_errors as $error) {
700 $feedback .= "{$error}\n"; //Errors are already localized
701 }
702 $feedback .= "</pre></div>";
703 }
704
705 // did we just save options for wp-cron?
706 if ( (function_exists('wp_schedule_event') || function_exists('wp_cron_init'))
707 && isset($_POST['wp_cron_backup_options']) ) :
708 if ( function_exists('wp_schedule_event') ) {
709 wp_clear_scheduled_hook( 'wp_db_backup_cron' ); // unschedule previous
710 $scheds = (array) wp_get_schedules();
711 $name = strval($_POST['wp_cron_schedule']);
712 $interval = ( isset($scheds[$name]['interval']) ) ?
713 (int) $scheds[$name]['interval'] : 0;
714 update_option('wp_cron_backup_schedule', $name, FALSE);
715 if ( ! 0 == $interval ) {
716 wp_schedule_event(time() + $interval, $name, 'wp_db_backup_cron');
717 }
718 }
719 else {
720 update_option('wp_cron_backup_schedule', intval($_POST['cron_schedule']), FALSE);
721 }
722 update_option('wp_cron_backup_tables', $_POST['wp_cron_backup_tables']);
723 if (is_email($_POST['cron_backup_recipient'])) {
724 update_option('wp_cron_backup_recipient', $_POST['cron_backup_recipient'], FALSE);
725 }
726 $feedback .= '<div class="updated"><p>' . __('Scheduled Backup Options Saved!') . '</p></div>';
727 endif;
728
729 $other_tables = array();
730 $also_backup = array();
731
732 // Get complete db table list
733 $all_tables = $wpdb->get_results("SHOW TABLES", ARRAY_N);
734 $all_tables = array_map(create_function('$a', 'return $a[0];'), $all_tables);
735 // Get list of WP tables that actually exist in this DB (for 1.6 compat!)
736 $wp_backup_default_tables = array_intersect($all_tables, $this->core_table_names);
737 // Get list of non-WP tables
738 $other_tables = array_diff($all_tables, $wp_backup_default_tables);
739
740 if ('' != $feedback) {
741 echo $feedback;
742 }
743
744 // Give the new dirs the same perms as wp-content.
745 $stat = stat( ABSPATH . 'wp-content' );
746 $dir_perms = $stat['mode'] & 0000777; // Get the permission bits.
747
748 if ( !file_exists( ABSPATH . $this->backup_dir) ) {
749 if ( @ mkdir( ABSPATH . $this->backup_dir) ) {
750 @ chmod( ABSPATH . $this->backup_dir, $dir_perms);
751 } else {
752 echo '<div class="updated error"><p align="center">' . __('WARNING: Your wp-content directory is <strong>NOT</strong> writable! We can not create the backup directory.') . '<br />' . ABSPATH . $this->backup_dir . "</p></div>";
753 $WHOOPS = TRUE;
754 }
755 }
756
757 if ( !is_writable( ABSPATH . $this->backup_dir) ) {
758 echo '<div class="updated error"><p align="center">' . __('WARNING: Your backup directory is <strong>NOT</strong> writable! We can not create the backup directory.') . '<br />' . ABSPATH . "</p></div>";
759 }
760
761 if ( !file_exists( ABSPATH . $this->backup_dir . 'index.php') ) {
762 @ touch( ABSPATH . $this->backup_dir . "index.php");
763 }
764
765 ?><div class='wrap'>
766 <h2><?php _e('Backup') ?></h2>
767 <form method="post" action="">
768 <fieldset class="options"><legend><?php _e('Tables') ?></legend>
769 <table align="center" cellspacing="5" cellpadding="5"><tr><td width="50%" align="left" class="alternate" valign="top">
770 <?php _e('These core WordPress tables will always be backed up:') ?><br /><ul><?php
771 foreach ($wp_backup_default_tables as $table) {
772 echo "<li><input type='hidden' name='core_tables[]' value='$table' />$table</li>";
773 }
774 ?></ul></td><td width="50%" align="left" valign="top"><?php
775 if (count($other_tables) > 0) {
776 echo __('You may choose to include any of the following tables:') . ' <br />';
777 foreach ($other_tables as $table) {
778 echo "<label style=\"display:block;\"><input type='checkbox' name='other_tables[]' value='{$table}' /> {$table}</label>";
779 }
780 }
781 ?></td></tr></table></fieldset>
782 <fieldset class="options"><legend><?php _e('Backup Options') ?></legend>
783 <?php _e('What to do with the backup file:') ?><br />
784 <label for="do_save" style="display:block;"><input type="radio" id="do_save" name="deliver" value="none" style="border:none;" />
785 <?php echo __('Save to server') . " ({$this->backup_dir})</label>";
786 ?><label for="do_download" style="display:block;"><input type="radio" checked="checked" id="do_download" name="deliver" value="http" style="border:none;" />
787 <?php _e('Download to your computer') ?></label>
788 <label for="do_email"><input type="radio" name="deliver" id="do_email" value="smtp" style="border:none;" />
789 <?php _e('Email backup to:') ?></label><input type="text" name="backup_recipient" size="20" value="<?php echo get_option('admin_email') ?>" />
790
791 <?php
792 // Check DB dize.
793 $table_status = $wpdb->get_results("SHOW TABLE STATUS FROM " . $this->backquote(DB_NAME));
794 $core_size = $db_size = 0;
795 foreach($table_status as $table) {
796 $table_size = $table->Data_length - $table->Data_free;
797 if(in_array($table->Name, $wp_backup_default_tables)) {
798 $core_size += $table_size;
799 }
800 $db_size += $table_size;
801 }
802 $mem_limit = ini_get('memory_limit');
803 $mem_limit = $this->return_bytes($mem_limit);
804 $mem_limit = ($mem_limit == 0) ? 8*1024*1024 : $mem_limit - 2000000;
805
806 if (! $WHOOPS) {
807 echo '<input type="hidden" name="do_backup" id="do_backup" value="backup" />';
808 echo '<p class="submit"><input type="submit" name="submit" onclick="document.getElementById(\'do_backup\').value=\'fragments\';" value="' . __('Backup') . '!" /></p>';
809 } else {
810 echo '<p class="alternate">' . __('WARNING: Your backup directory is <strong>NOT</strong> writable!') . '</p>';
811 }
812 echo '</fieldset>';
813 echo '</form>';
814
815 // this stuff only displays if some sort of wp-cron is available
816 $cron = ( function_exists('wp_schedule_event') ) ? true : false; // wp-cron in WP 2.1+
817 $cron_old = ( function_exists('wp_cron_init') && ! $cron ) ? true : false; // wp-cron plugin by Skippy
818 if ( $cron_old || $cron ) :
819 echo '<fieldset class="options"><legend>' . __('Scheduled Backup') . '</legend>';
820 $datetime = get_option('date_format') . ' @ ' . get_option('time_format');
821 if ( $cron ) :
822 if ( ! ( 'never' == $this->get_sched() ) ) :
823 echo '<p>' . __('Next Backup') . ': ';
824 echo gmdate($datetime, wp_next_scheduled('wp_db_backup_cron') + (get_option('gmt_offset') * 3600)) . '</p>';
825 endif;
826 elseif ( $cron_old ) :
827 echo '<p>' . __('Last WP-Cron Daily Execution') . ': ' . gmdate($datetime, get_option('wp_cron_daily_lastrun') + (get_option('gmt_offset') * 3600)) . '<br />';
828 echo __('Next WP-Cron Daily Execution') . ': ' . gmdate($datetime, (get_option('wp_cron_daily_lastrun') + (get_option('gmt_offset') * 3600) + 86400)) . '</p>';
829 endif;
830 ?><form method="post" action="">
831 <table width="100%" cellpadding="5" cellspacing="5">
832 <tr><td align="center"><?php
833 echo __('Schedule: ');
834 if ( $cron_old ) :
835 $wp_cron_backup_schedule = get_option('wp_cron_backup_schedule');
836 $schedule = array(0 => __('None'), 1 => __('Daily'));
837 foreach ($schedule as $value => $name) {
838 echo ' <input type="radio" style="border:none;" name="cron_schedule"';
839 if ($wp_cron_backup_schedule == $value) {
840 echo ' checked="checked" ';
841 }
842 echo 'value="' . $value . '" /> ' . __($name);
843 }
844 elseif ( $cron ) :
845 echo apply_filters('wp_db_b_schedule_choices', wp_get_schedules() );
846 endif;
847 echo '</td><td align="center">';
848 $cron_recipient = get_option('wp_cron_backup_recipient');
849 if (! is_email($cron_recipient)) {
850 $cron_recipient = get_option('admin_email');
851 }
852 echo __('Email backup to:') . ' <input type="text" name="cron_backup_recipient" size="20" value="' . $cron_recipient . '" />';
853 echo '</td></tr>';
854 $cron_tables = get_option('wp_cron_backup_tables');
855 if (! is_array($cron_tables)) {
856 $cron_tables = array();
857 }
858 if (count($other_tables) > 0) {
859 echo '<tr><td colspan="2" align="left">' . __('Tables to include:') . '<br />';
860 foreach ($other_tables as $table) {
861 echo '<input type="checkbox" ';
862 if (in_array($table, $cron_tables)) {
863 echo 'checked=checked ';
864 }
865 echo "name='wp_cron_backup_tables[]' value='{$table}' /> {$table}<br />";
866 }
867 echo '</td></tr>';
868 }
869 echo '<tr><td colspan="2" align="center"><input type="hidden" name="wp_cron_backup_options" value="SET" /><input type="submit" name="submit" value="' . __('Submit') . '" /></td></tr></table></form>';
870 echo '</fieldset>';
871 endif; // end of wp_cron (legacy) section
872
873 echo '</div>';
874
875 } // end wp_backup_menu()
876
877 function get_sched() {
878 $options = array_keys( (array) wp_get_schedules() );
879 $freq = get_option('wp_cron_backup_schedule');
880 $freq = ( in_array( $freq , $options ) ) ? $freq : 'never';
881 return $freq;
882 }
883
884 function schedule_choices($schedule) { // create the cron menu based on the schedule
885 $wp_cron_backup_schedule = $this->get_sched();
886 $sort = array();
887 foreach ( (array) $schedule as $key => $value ) $sort[$key] = $value['interval'];
888 asort( $sort );
889 $schedule_sorted = array();
890 foreach ( (array) $sort as $key => $value ) $schedule_sorted[$key] = $schedule[$key];
891 $menu = '<ul style="list-style: none; text-align: left">';
892 $schedule = array_merge( array( 'never' => array( 'interval' => 0, 'display' => __('Never') ) ),
893 (array) $schedule_sorted );
894 foreach ( $schedule as $name => $settings) {
895 $interval = (int) $settings['interval'];
896 if ( 0 == $interval && ! 'never' == $name ) continue;
897 $display = ( ! '' == $settings['display'] ) ? $settings['display'] : sprintf(__('%s seconds'),$interval);
898 $menu .= "<li><input type='radio' name='wp_cron_schedule' style='border:none;'";
899 if ($wp_cron_backup_schedule == $name) {
900 $menu .= ' checked="checked" ';
901 }
902 $menu .= "value='$name' /> $display</li>";
903 }
904 $menu .= '</ul>';
905 return $menu;
906 } // end schedule_choices()
907
908 function wp_cron_daily() { // for legacy cron plugin
909 $schedule = intval(get_option('wp_cron_backup_schedule'));
910 if (0 == $schedule) {
911 // Scheduled backup is disabled
912 return;
913 }
914 else { $this->cron_backup(); }
915 } // wp_cron_daily
916
917 function cron_backup() {
918 global $table_prefix, $wpdb;
919
920 $all_tables = $wpdb->get_results("SHOW TABLES", ARRAY_N);
921 $all_tables = array_map(create_function('$a', 'return $a[0];'), $all_tables);
922 $core_tables = array_intersect($all_tables, $this->core_table_names);
923 $other_tables = get_option('wp_cron_backup_tables');
924
925 $recipient = get_option('wp_cron_backup_recipient');
926
927 $backup_file = $this->db_backup($core_tables, $other_tables);
928 if (FALSE !== $backup_file) {
929 $this->deliver_backup ($backup_file, 'smtp', $recipient);
930 }
931 return;
932 }
933
934 function add_sched_options($sched) {
935 $sched['weekly'] = array('interval' => 604800, 'display' => __('Once Weekly'));
936 return $sched;
937 }
938
939 function user_can_backup() {
940 return current_user_can('import');
941 }
942
943 function validate_file($file) {
944 if (false !== strpos($file, '..'))
945 die(__("Cheatin' uh ?"));
946
947 if (false !== strpos($file, './'))
948 die(__("Cheatin' uh ?"));
949
950 if (':' == substr($file, 1, 1))
951 die(__("Cheatin' uh ?"));
952 }
953
954 }
955
956 function wpdbBackup_init() {
957 global $mywpdbbackup;
958 $mywpdbbackup = new wpdbBackup();
959 }
960
961 add_action('plugins_loaded', 'wpdbBackup_init');
962
963 ?>
964