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