root/tags/2.0.5/wp-content/plugins/wp-db-backup.php

Revision 4248, 30.7 kB (checked in by ryan, 2 years ago)

Bump ver. fixes #3165

  • Property svn:eol-style set to native
Line 
1 <?php
2 /*
3 Plugin Name: WordPress Database Backup
4 Plugin URI: http://www.skippy.net/blog/plugins/
5 Description: On-demand backup of your WordPress database.
6 Author: Scott Merrill
7 Version: 1.8
8 Author URI: http://www.skippy.net/
9
10 Much of this was modified from Mark Ghosh's One Click Backup, which
11 in turn was derived from phpMyAdmin.
12
13 Many thanks to Owen (http://asymptomatic.net/wp/) for his patch
14    http://dev.wp-plugins.org/ticket/219
15 */
16
17 // CHANGE THIS IF YOU WANT TO USE A
18 // DIFFERENT BACKUP LOCATION
19
20 $rand = substr( md5( md5( DB_PASSWORD ) ), -5 );
21
22 define('WP_BACKUP_DIR', 'wp-content/backup-' . $rand);
23
24 define('ROWS_PER_SEGMENT', 100);
25
26 class wpdbBackup {
27
28     var $backup_complete = false;
29     var $backup_file = '';
30     var $backup_dir = WP_BACKUP_DIR;
31     var $backup_errors = array();
32     var $basename;
33
34     function gzip() {
35         return function_exists('gzopen');
36     }
37
38     function wpdbBackup() {
39         add_action('wp_cron_daily', array(&$this, 'wp_cron_daily'));
40
41         $this->backup_dir = trailingslashit($this->backup_dir);
42         $this->basename = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', __FILE__);
43     
44         if (isset($_POST['do_backup'])) {
45             if ( !current_user_can('import') ) die(__('You are not allowed to perform backups.'));
46             switch($_POST['do_backup']) {
47             case 'backup':
48                 $this->perform_backup();
49                 break;
50             case 'fragments':
51                 add_action('admin_menu', array(&$this, 'fragment_menu'));
52                 break;               
53             }
54         } elseif (isset($_GET['fragment'] )) {
55             if ( !current_user_can('import') ) die(__('You are not allowed to perform backups.'));
56             add_action('init', array(&$this, 'init'));
57         } elseif (isset($_GET['backup'] )) {
58             if ( !current_user_can('import') ) die(__('You are not allowed to perform backups.'));
59             add_action('init', array(&$this, 'init'));
60         } else {
61             add_action('admin_menu', array(&$this, 'admin_menu'));
62         }
63     }
64     
65     function init() {
66         if ( !current_user_can('import') ) die(__('You are not allowed to perform backups.'));
67
68         if (isset($_GET['backup'])) {
69             $via = isset($_GET['via']) ? $_GET['via'] : 'http';
70             
71             $this->backup_file = $_GET['backup'];
72             $this->validate_file($this->backup_file);
73
74             switch($via) {
75             case 'smtp':
76             case 'email':
77                 $this->deliver_backup ($this->backup_file, 'smtp', $_GET['recipient']);
78                 echo '
79                     <!-- ' . $via . ' -->
80                     <script type="text/javascript"><!--\\
81                 ';
82                 if($this->backup_errors) {
83                     foreach($this->backup_errors as $error) {
84                         echo "window.parent.addError('$error');\n";
85                     }
86                 }
87                 echo '
88                     alert("' . __('Backup Complete!') . '");
89                     </script>
90                 ';
91                 break;
92             default:
93                 $this->deliver_backup ($this->backup_file, $via);
94             }
95             die();
96         }
97         if (isset($_GET['fragment'] )) {
98             list($table, $segment, $filename) = explode(':', $_GET['fragment']);
99             $this->validate_file($filename);
100             $this->backup_fragment($table, $segment, $filename);
101         }
102
103         die();
104     }
105     
106     function build_backup_script() {
107         global $table_prefix, $wpdb;
108     
109         $datum = date("Ymd_B");
110         $backup_filename = DB_NAME . "_$table_prefix$datum.sql";
111         if ($this->gzip()) $backup_filename .= '.gz';
112         
113         echo "<div class='wrap'>";
114         //echo "<pre>" . print_r($_POST, 1) . "</pre>";
115         echo '<h2>' . __('Backup') . '</h2>
116             <fieldset class="options"><legend>' . __('Progress') . '</legend>
117             <p><strong>' .
118                 __('DO NOT DO THE FOLLOWING AS IT WILL CAUSE YOUR BACKUP TO FAIL:').
119             '</strong></p>
120             <ol>
121                 <li>'.__('Close this browser').'</li>
122                 <li>'.__('Reload this page').'</li>
123                 <li>'.__('Click the Stop or Back buttons in your browser').'</li>
124             </ol>
125             <p><strong>' . __('Progress:') . '</strong></p>
126             <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>
127             <div id="progress_message"></div>
128             <div id="errors"></div>
129             </fieldset>
130             <iframe id="backuploader" src="about:blank" style="border:0px solid white;height:1em;width:1em;"></iframe>
131             <script type="text/javascript"><!--//
132             function setMeter(pct) {
133                 var meter = document.getElementById("meter");
134                 meter.style.width = pct + "%";
135                 meter.innerHTML = Math.floor(pct) + "%";
136             }
137             function setProgress(str) {
138                 var progress = document.getElementById("progress_message");
139                 progress.innerHTML = str;
140             }
141             function addError(str) {
142                 var errors = document.getElementById("errors");
143                 errors.innerHTML = errors.innerHTML + str + "<br />";
144             }
145
146             function backup(table, segment) {
147                 var fram = document.getElementById("backuploader");               
148                 fram.src = "' . $_SERVER['REQUEST_URI'] . '&fragment=" + table + ":" + segment + ":' . $backup_filename . '";
149             }
150             
151             var curStep = 0;
152             
153             function nextStep() {
154                 backupStep(curStep);
155                 curStep++;
156             }
157             
158             function finishBackup() {
159                 var fram = document.getElementById("backuploader");               
160                 setMeter(100);
161         ';
162
163         $this_basename = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', __FILE__);
164         $download_uri = get_settings('siteurl') . "/wp-admin/edit.php?page={$this_basename}&backup={$backup_filename}";
165         switch($_POST['deliver']) {
166         case 'http':
167             echo '
168                 setProgress("' . sprintf(__("Backup complete, preparing <a href=\\\"%s\\\">backup</a> for download..."), $download_uri) . '");
169                 fram.src = "' . $download_uri . '";
170             ';
171             break;
172         case 'smtp':
173             echo '
174                 setProgress("' . sprintf(__("Backup complete, sending <a href=\\\"%s\\\">backup</a> via email..."), $download_uri) . '");
175                 fram.src = "' . $download_uri . '&via=email&recipient=' . $_POST['backup_recipient'] . '";
176             ';
177             break;
178         default:
179             echo '
180                 setProgress("' . sprintf(__("Backup complete, download <a href=\\\"%s\\\">here</a>."), $download_uri) . '");
181             ';
182         }
183         
184         echo '
185             }
186             
187             function backupStep(step) {
188                 switch(step) {
189                 case 0: backup("", 0); break;
190         ';
191         
192         $also_backup = array();
193         if (isset($_POST['other_tables'])) {
194             $also_backup = $_POST['other_tables'];
195         } else {
196             $also_backup = array();
197         }
198         $core_tables = $_POST['core_tables'];
199         $tables = array_merge($core_tables, $also_backup);
200         $step_count = 1;
201         foreach ($tables as $table) {
202             $rec_count = $wpdb->get_var("SELECT count(*) FROM {$table}");
203             $rec_segments = ceil($rec_count / ROWS_PER_SEGMENT);
204             $table_count = 0;
205             do {
206                 echo "case {$step_count}: backup(\"{$table}\", {$table_count}); break;\n";
207                 $step_count++;
208                 $table_count++;
209             } while($table_count < $rec_segments);
210             echo "case {$step_count}: backup(\"{$table}\", -1); break;\n";
211             $step_count++;
212         }
213         echo "case {$step_count}: finishBackup(); break;";
214         
215         echo '
216                 }
217                 if(step != 0) setMeter(100 * step / ' . $step_count . ');
218             }
219
220             nextStep();
221             //--></script>
222     </div>
223         ';
224     }
225
226     function backup_fragment($table, $segment, $filename) {
227         global $table_prefix, $wpdb;
228             
229         echo "$table:$segment:$filename";
230         
231         if($table == '') {
232             $msg = __('Creating backup file...');
233         } else {
234             if($segment == -1) {
235                 $msg = sprintf(__('Finished backing up table \\"%s\\".'), $table);
236             } else {
237                 $msg = sprintf(__('Backing up table \\"%s\\"...'), $table);
238             }
239         }
240         
241         echo '<script type="text/javascript"><!--//
242         var msg = "' . $msg . '";
243         window.parent.setProgress(msg);
244         ';
245             
246         if (is_writable(ABSPATH . $this->backup_dir)) {
247             $this->fp = $this->open(ABSPATH . $this->backup_dir . $filename, 'a');
248             if(!$this->fp) {
249                 $this->backup_error(__('Could not open the backup file for writing!'));
250                 $this->fatal_error = __('The backup file could not be saved.  Please check the permissions for writing to your backup directory and try again.');
251             }
252             else {
253                 if($table == '') {       
254                     //Begin new backup of MySql
255                     $this->stow("# WordPress MySQL database backup\n");
256                     $this->stow("#\n");
257                     $this->stow("# Generated: " . date("l j. F Y H:i T") . "\n");
258                     $this->stow("# Hostname: " . DB_HOST . "\n");
259                     $this->stow("# Database: " . $this->backquote(DB_NAME) . "\n");
260                     $this->stow("# --------------------------------------------------------\n");
261                 } else {
262                     if($segment == 0) {
263                         // Increase script execution time-limit to 15 min for every table.
264                         if ( !ini_get('safe_mode')) @set_time_limit(15*60);
265                         //ini_set('memory_limit', '16M');
266                         // Create the SQL statements
267                         $this->stow("# --------------------------------------------------------\n");
268                         $this->stow("# Table: " . $this->backquote($table) . "\n");
269                         $this->stow("# --------------------------------------------------------\n");
270                     }           
271                     $this->backup_table($table, $segment);
272                 }
273             }
274         } else {
275             $this->backup_error(__('The backup directory is not writeable!'));
276             $this->fatal_error = __('The backup directory is not writeable!  Please check the permissions for writing to your backup directory and try again.');
277         }
278
279         if($this->fp) $this->close($this->fp);
280         
281         if($this->backup_errors) {
282             foreach($this->backup_errors as $error) {
283                 echo "window.parent.addError('$error');\n";
284             }
285         }
286         if($this->fatal_error) {
287             echo '
288                 alert("' . addslashes($this->fatal_error) . '");
289                 //--></script>
290             ';
291         }
292         else {
293             echo '
294                 window.parent.nextStep();
295                 //--></script>
296             ';
297         }
298         
299         die();
300     }
301
302     function perform_backup() {
303         // are we backing up any other tables?
304         $also_backup = array();
305         if (isset($_POST['other_tables'])) {
306             $also_backup = $_POST['other_tables'];
307         }
308         
309         $core_tables = $_POST['core_tables'];
310         $this->backup_file = $this->db_backup($core_tables, $also_backup);
311         if (FALSE !== $this->backup_file) {
312             if ('smtp' == $_POST['deliver']) {
313                 $this->deliver_backup ($this->backup_file, $_POST['deliver'], $_POST['backup_recipient']);
314             } elseif ('http' == $_POST['deliver']) {
315                 $this_basename = preg_replace('/^.*wp-content[\\\\\/]plugins[\\\\\/]/', '', __FILE__);
316                 header('Refresh: 3; ' . get_settings('siteurl') . "/wp-admin/edit.php?page={$this_basename}&backup={$this->backup_file}");
317             }
318             // we do this to say we're done.
319             $this->backup_complete = true;
320         }
321     }
322     
323     ///////////////////////////////
324     function admin_menu() {
325         add_management_page(__('Backup'), __('Backup'), 'import', basename(__FILE__), array(&$this, 'backup_menu'));
326     }
327
328     function fragment_menu() {
329         add_management_page(__('Backup'), __('Backup'), 'import', basename(__FILE__), array(&$this, 'build_backup_script'));
330     }
331
332     /////////////////////////////////////////////////////////
333     function sql_addslashes($a_string = '', $is_like = FALSE)
334     {
335             /*
336                     Better addslashes for SQL queries.
337                     Taken from phpMyAdmin.
338             */
339         if ($is_like) {
340             $a_string = str_replace('\\', '\\\\\\\\', $a_string);
341         } else {
342             $a_string = str_replace('\\', '\\\\', $a_string);
343         }
344         $a_string = str_replace('\'', '\\\'', $a_string);
345
346         return $a_string;
347     } // function sql_addslashes($a_string = '', $is_like = FALSE)
348
349     ///////////////////////////////////////////////////////////
350     function backquote($a_name)
351     {
352             /*
353                     Add backqouotes to tables and db-names in
354                     SQL queries. Taken from phpMyAdmin.
355             */
356         if (!empty($a_name) && $a_name != '*') {
357             if (is_array($a_name)) {
358                  $result = array();
359                  reset($a_name);
360                  while(list($key, $val) = each($a_name)) {
361                      $result[$key] = '`' . $val . '`';
362                  }
363                  return $result;
364             } else {
365                 return '`' . $a_name . '`';
366             }
367         } else {
368             return $a_name;
369         }
370     } // function backquote($a_name, $do_it = TRUE)
371
372     /////////////
373     function open($filename = '', $mode = 'w') {
374         if ('' == $filename) return false;
375         if ($this->gzip()) {
376             $fp = @gzopen($filename, $mode);
377         } else {
378             $fp = @fopen($filename, $mode);
379         }
380         return $fp;
381     }
382
383     //////////////
384     function close($fp) {
385         if ($this->gzip()) {
386             gzclose($fp);
387         } else {
388             fclose($fp);
389         }
390     }
391     
392     //////////////
393     function stow($query_line) {
394         if ($this->gzip()) {
395             if(@gzwrite($this->fp, $query_line) === FALSE) {
396                 backup_error(__('There was an error writing a line to the backup script:'));
397                 backup_error('&nbsp;&nbsp;' . $query_line);
398             }
399         } else {
400             if(@fwrite($this->fp, $query_line) === FALSE) {
401                 backup_error(__('There was an error writing a line to the backup script:'));
402                 backup_error('&nbsp;&nbsp;' . $query_line);
403             }
404         }
405     }
406     
407     function backup_error($err) {
408         if(count($this->backup_errors) < 20) {
409             $this->backup_errors[] = $err;
410         } elseif(count($this->backup_errors) == 20) {
411             $this->backup_errors[] = __('Subsequent errors have been omitted from this log.');
412         }
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             //
436             // Add SQL statement to drop existing table
437             $this->stow("\n\n");
438             $this->stow("#\n");
439             $this->stow("# Delete any existing table " . $this->backquote($table) . "\n");
440             $this->stow("#\n");
441             $this->stow("\n");
442             $this->stow("DROP TABLE IF EXISTS " . $this->backquote($table) . ";\n");
443             
444             //
445             //Table structure
446             // Comment in SQL-file
447             $this->stow("\n\n");
448             $this->stow("#\n");
449             $this->stow("# Table structure of table " . $this->backquote($table) . "\n");
450             $this->stow("#\n");
451             $this->stow("\n");
452             
453             $create_table = $wpdb->get_results("SHOW CREATE TABLE $table", ARRAY_N);
454             if (FALSE === $create_table) {
455                 $this->backup_error(sprintf(__("Error with SHOW CREATE TABLE for %s."), $table));
456                 $this->stow("#\n# Error with SHOW CREATE TABLE for $table!\n#\n");
457             }
458             $this->stow($create_table[0][1] . ' ;');
459             
460             if (FALSE === $table_structure) {
461                 $this->backup_error(sprintf(__("Error getting table structure of %s"), $table));
462                 $this->stow("#\n# Error getting table structure of $table!\n#\n");
463             }
464         
465             //
466             // Comment in SQL-file
467             $this->stow("\n\n");
468             $this->stow("#\n");
469             $this->stow('# Data contents of table ' . $this->backquote($table) . "\n");
470             $this->stow("#\n");
471         }
472         
473         if(($segment == 'none') || ($segment >= 0)) {
474             $ints = array();
475             foreach ($table_structure as $struct) {
476                 if ( (0 === strpos($struct->Type, 'tinyint')) ||
477                     (0 === strpos(strtolower($struct->Type), 'smallint')) ||
478                     (0 === strpos(strtolower($struct->Type), 'mediumint')) ||
479                     (0 === strpos(strtolower($struct->Type), 'int')) ||
480                     (0 === strpos(strtolower($struct->Type), 'bigint')) ||
481