3 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
\r
4 * Copyright (C) 2002-2012 The Nucleus Group
\r
6 * This program is free software; you can redistribute it and/or
\r
7 * modify it under the terms of the GNU General Public License
\r
8 * as published by the Free Software Foundation; either version 2
\r
9 * of the License, or (at your option) any later version.
\r
10 * (see nucleus/documentation/index.html#license for more info)
\r
13 * Scripts to create/restore a backup of the Nucleus database
\r
15 * @license http://nucleuscms.org/license.txt GNU General Public License
\r
16 * @copyright Copyright (C) 2002-2012 The Nucleus Group
\r
17 * @version $Id: backup.php 1624 2012-01-09 11:36:20Z sakamocchi $
\r
24 * Constructor, just for compatibility
\r
31 public function Backup()
\r
37 * Backup::do_backup()
\r
38 * This function creates an sql dump of the database and sends it to
\r
39 * the user as a file (can be gzipped if they want)
\r
41 * NOTE: this remains not-static for compatibility
\r
43 * @param boolean $gzip 1 = compress backup file, 0 = no compression (default)
\r
47 public function do_backup($gzip = 0)
\r
49 global $manager, $nucleus;
\r
51 // tables of which backup is needed
\r
53 sql_table('actionlog'),
\r
56 sql_table('comment'),
\r
57 sql_table('config'),
\r
60 sql_table('member'),
\r
62 sql_table('skin_desc'),
\r
64 sql_table('template'),
\r
65 sql_table('template_desc'),
\r
66 sql_table('plugin'),
\r
67 sql_table('plugin_event'),
\r
68 sql_table('plugin_option'),
\r
69 sql_table('plugin_option_desc'),
\r
70 sql_table('category'),
\r
71 sql_table('activation'),
\r
72 sql_table('tickets'),
\r
75 // add tables that plugins want to backup to the list
\r
76 // catch all output generated by plugins
\r
78 $res = sql_query('SELECT pfile FROM ' . sql_table('plugin'));
\r
79 while ( $plugName = sql_fetch_object($res) )
\r
81 $plug =& $manager->getPlugin($plugName->pfile);
\r
84 $tables = array_merge($tables, (array) $plug->getTableList());
\r
89 // remove duplicates
\r
90 $tables = array_unique($tables);
\r
92 // make sure browsers don't cache the backup
\r
93 header("Pragma: no-cache");
\r
95 // don't allow gzip compression when extension is not loaded
\r
96 if ( ($gzip != 0) && !extension_loaded("zlib") )
\r
103 $filename = 'nucleus_db_backup_' . i18n::formatted_datetime('%Y-%m-%d-%H-%M-%S', time()) . ".sql";
\r
107 // use an output buffer
\r
109 @ob_implicit_flush(0);
\r
112 $filename = 'nucleus_db_backup_' . i18n::formatted_datetime('%Y-%m-%d-%H-%M-%S', time()) . ".sql.gz";
\r
115 // send headers that tell the browser a file is coming
\r
116 header("Content-Type: text/x-delimtext; name=\"$filename\"");
\r
117 header("Content-disposition: attachment; filename=$filename");
\r
121 echo " * This is a backup file generated by Nucleus \n";
\r
122 echo " * http://www.nucleuscms.org/\n";
\r
124 echo " * backup-date: " . i18n::formatted_datetime('rfc822GMT', time()) . "\n";
\r
125 echo " * Nucleus CMS version: " . $nucleus['version'] . "\n";
\r
127 echo " * WARNING: Only try to restore on servers running the exact same version of Nucleus\n";
\r
132 array_walk($tables, array(__CLASS__, 'dump_table'));
\r
136 $Size = ob_get_length();
\r
137 $Crc = crc32(ob_get_contents());
\r
138 $contents = gzcompress(ob_get_contents());
\r
140 echo "\x1f\x8b\x08\x00\x00\x00\x00\x00" . substr($contents, 0, strlen($contents) - 4)
\r
141 . self::gzip_print_four_characters($Crc) . self::gzip_print_four_characters($Size);
\r
147 * Backup::dump_table()
\r
148 * Creates a dump for a single table
\r
149 * ($tablename and $key are filled in by array_walk)
\r
152 * @param string $tablename
\r
153 * @param string $key
\r
155 static private function dump_table($tablename, $key)
\r
158 echo " * TABLE: " . $tablename . "\n";
\r
161 // dump table structure
\r
162 self::dump_structure($tablename);
\r
164 // dump table contents
\r
165 self::dump_contents($tablename);
\r
170 * Backup::dump_structure()
\r
171 * Creates a dump of the table structure for one table
\r
174 * @param string $tablename
\r
178 static private function dump_structure($tablename)
\r
180 // add command to drop table on restore
\r
181 echo "DROP TABLE IF EXISTS `$tablename`;\n\n";
\r
182 $result = sql_query("SHOW CREATE TABLE $tablename");
\r
183 $create = sql_fetch_assoc($result);
\r
184 echo $create['Create Table'];
\r
190 * Backup::get_field_names()
\r
191 * Returns the field named for the given table in the
\r
192 * following format:
\r
193 * (column1, column2, ..., columnn)
\r
196 * @param resource $result
\r
197 * @param integer $num_fields
\r
200 static private function get_field_names($result, $num_fields)
\r
203 for ( $j = 0; $j < $num_fields; $j++ )
\r
205 $fields[] = sql_field_name($result, $j);
\r
208 return '(' . implode(', ', $fields) . ')';
\r
212 * Backup::dump_contents()
\r
213 * Creates a dump of the table content for one table
\r
216 * @param string $tablename
\r
220 static private function dump_contents($tablename)
\r
223 * Grab the data from the table.
\r
225 $result = sql_query("SELECT * FROM $tablename");
\r
227 if ( sql_num_rows($result) > 0 )
\r
231 echo " * Table Data for {$tablename}\n";
\r
235 $num_fields = sql_num_fields($result);
\r
238 * Compose fieldname list
\r
240 $tablename_list = self::get_field_names($result, $num_fields);
\r
243 * Loop through the resulting rows and build the sql statement.
\r
245 while ( $row = sql_fetch_array($result) )
\r
247 // Start building the SQL statement.
\r
248 echo "INSERT INTO ".$tablename." $tablename_list VALUES(";
\r
250 // Loop through the rows and fill in data for each column
\r
251 for ( $j = 0; $j < $num_fields; $j++ )
\r
253 if ( !isset($row[$j]) )
\r
255 // no data for column
\r
258 elseif ( $row[$j] != '' )
\r
261 echo " '" . sql_real_escape_string($row[$j]) . "'";
\r
265 // empty column (!= no data!)
\r
269 // only add comma when not last column
\r
270 if ( $j != ($num_fields - 1) )
\r
282 * Backup::gzip_print_four_characters()
\r
285 * @param integer $val
\r
288 static private function gzip_print_four_characters($Val)
\r
290 for ( $i = 0; $i < 4; $i ++ )
\r
292 $return .= chr($Val % 256);
\r
293 $Val = floor($Val / 256);
\r
299 * Backup::do_restore()
\r
300 * Restores a database backup
\r
302 * NOTE: this remains not-static for compatibility
\r
307 public function do_restore()
\r
309 $uploadInfo = postFileInfo('backup_file');
\r
311 // first of all: get uploaded file:
\r
312 if ( array_key_exists('name', $uploadInfo) && empty($uploadInfo['name']) )
\r
314 return 'No file uploaded';
\r
316 if ( !is_uploaded_file($uploadInfo['tmp_name']) )
\r
318 return 'No file uploaded';
\r
321 $backup_file_name = $uploadInfo['name'];
\r
322 $backup_file_tmpname = $uploadInfo['tmp_name'];
\r
323 $backup_file_type = $uploadInfo['type'];
\r
325 if ( !file_exists($backup_file_tmpname) )
\r
327 return 'File Upload Error';
\r
330 if ( !preg_match("#^(text/[a-zA-Z]+)|(application/(x\-)?gzip(\-compressed)?)|(application/octet-stream)$#i", $backup_file_type) )
\r
332 return 'The uploaded file is not of the correct type';
\r
336 if ( preg_match("#\.gz#i", $backup_file_name) )
\r
341 if ( !extension_loaded("zlib") && $gzip )
\r
343 return 'Cannot decompress gzipped backup (zlib package not installed)';
\r
346 // get sql query according to gzip setting (either decompress, or not)
\r
347 $contents = self::get_contents($backup_file_tmpname, $gzip);
\r
348 if ( $contents == '' )
\r
350 return 'Cannot get contents from this file.';
\r
354 $lines = preg_split('/[\r\n]/', $contents);
\r
355 if( $lines === $contents )
\r
357 return 'Cannot parse contents from this file';
\r
360 /* get sql statements from each lines */
\r
361 $queries = self::get_queries($lines);
\r
362 if ( $queries === array() )
\r
364 return "Cannot get SQL queries from this file.";
\r
367 /* execute sql statements */
\r
368 foreach ( $queries as $query )
\r
370 if ( !sql_query($query) )
\r
372 debug('SQL Error: ' . sql_error());
\r
380 static private function get_contents($temporary_name, $gzip = 0)
\r
385 // decompress and read
\r
386 $gz_ptr = gzopen($temporary_name, 'rb');
\r
387 while ( !gzeof($gz_ptr) )
\r
389 $contents .= gzgets($gz_ptr, 100000);
\r
395 $fsize = filesize($temporary_name);
\r
398 $contents = fread(fopen($temporary_name, 'r'), $fsize);
\r
404 static private function get_queries($lines)
\r
407 $queries = array();
\r
408 foreach ( $lines as $line )
\r
410 $line = trim($line);
\r
411 if ( !$line || $line[0] == '#' || preg_match('#^[\s|/]?\*#', $line) )
\r
416 if ( preg_match('/^(.*);$/', $line, $matches) === 0 )
\r
422 $query .= $matches[1];
\r
423 $queries[] = $query;
\r