OSDN Git Service

MERGE: リビジョン1778/1779のマージ。Skin::getFriendlyNames()のアクセス方法の変更
[nucleus-jp/nucleus-next.git] / nucleus / libs / backup.php
1 <?php\r
2 /*\r
3  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)\r
4  * Copyright (C) 2002-2012 The Nucleus Group\r
5  *\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
11  */\r
12 /**\r
13  * Scripts to create/restore a backup of the Nucleus database\r
14  *\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
18  */\r
19 \r
20 class Backup\r
21 {\r
22         /**\r
23          * Backup::Backup()\r
24          * Constructor, just for compatibility\r
25          *\r
26          * @deprecated\r
27          * @param       void\r
28          * @return      void\r
29          *\r
30          */\r
31         public function Backup()\r
32         {\r
33                 return;\r
34         }\r
35 \r
36         /**\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
40          *\r
41          * NOTE: this remains not-static for compatibility\r
42          *\r
43          * @param boolean       $gzip   1 = compress backup file, 0 = no compression (default)\r
44          * @return      void\r
45          *\r
46          */\r
47         public function do_backup($gzip = 0)\r
48         {\r
49                 global $manager, $nucleus;\r
50 \r
51                 // tables of which backup is needed\r
52                 $tables = array(\r
53                                 sql_table('actionlog'),\r
54                                 sql_table('ban'),\r
55                                 sql_table('blog'),\r
56                                 sql_table('comment'),\r
57                                 sql_table('config'),\r
58                                 sql_table('item'),\r
59                                 sql_table('karma'),\r
60                                 sql_table('member'),\r
61                                 sql_table('skin'),\r
62                                 sql_table('skin_desc'),\r
63                                 sql_table('team'),\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
73                 );\r
74 \r
75                 // add tables that plugins want to backup to the list\r
76                 // catch all output generated by plugins\r
77                 ob_start();\r
78                 $res = sql_query('SELECT pfile FROM ' . sql_table('plugin'));\r
79                 while ( $plugName = sql_fetch_object($res) )\r
80                 {\r
81                         $plug =& $manager->getPlugin($plugName->pfile);\r
82                         if ( $plug )\r
83                         {\r
84                                 $tables = array_merge($tables, (array) $plug->getTableList());\r
85                         }\r
86                 }\r
87                 ob_end_clean();\r
88 \r
89                 // remove duplicates\r
90                 $tables = array_unique($tables);\r
91 \r
92                 // make sure browsers don't cache the backup\r
93                 header("Pragma: no-cache");\r
94 \r
95                 // don't allow gzip compression when extension is not loaded\r
96                 if ( ($gzip != 0) && !extension_loaded("zlib") )\r
97                 {\r
98                         $gzip = 0;\r
99                 }\r
100 \r
101                 if ( !$gzip )\r
102                 {\r
103                         $filename = 'nucleus_db_backup_' . i18n::formatted_datetime('%Y-%m-%d-%H-%M-%S', time()) . ".sql";\r
104                 }\r
105                 else\r
106                 {\r
107                         // use an output buffer\r
108                         @ob_start();\r
109                         @ob_implicit_flush(0);\r
110 \r
111                         // set filename\r
112                         $filename = 'nucleus_db_backup_' . i18n::formatted_datetime('%Y-%m-%d-%H-%M-%S', time()) . ".sql.gz";\r
113                 }\r
114 \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
118 \r
119                 // dump header\r
120                 echo "/*\n";\r
121                 echo " * This is a backup file generated by Nucleus \n";\r
122                 echo " * http://www.nucleuscms.org/\n";\r
123                 echo " * \n";\r
124                 echo " * backup-date: " . i18n::formatted_datetime('rfc822GMT', time()) . "\n";\r
125                 echo " * Nucleus CMS version: " . $nucleus['version'] . "\n";\r
126                 echo " * \n";\r
127                 echo " * WARNING: Only try to restore on servers running the exact same version of Nucleus\n";\r
128                 echo " */\n";\r
129 \r
130                 // dump all tables\r
131                 reset($tables);\r
132                 array_walk($tables, array(__CLASS__, 'dump_table'));\r
133 \r
134                 if ( $gzip )\r
135                 {\r
136                         $Size = ob_get_length();\r
137                         $Crc = crc32(ob_get_contents());\r
138                         $contents = gzcompress(ob_get_contents());\r
139                         ob_end_clean();\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
142                 }\r
143                 exit;\r
144         }\r
145 \r
146         /**\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
150          *\r
151          * @static\r
152          * @param       string  $tablename\r
153          * @param       string  $key\r
154          */\r
155         static private function dump_table($tablename, $key)\r
156         {\r
157                 echo "/*\n";\r
158                 echo " * TABLE: " . $tablename . "\n";\r
159                 echo " */\n";\r
160 \r
161                 // dump table structure\r
162                 self::dump_structure($tablename);\r
163 \r
164                 // dump table contents\r
165                 self::dump_contents($tablename);\r
166                 return;\r
167         }\r
168 \r
169         /**\r
170          * Backup::dump_structure()\r
171          * Creates a dump of the table structure for one table\r
172          *\r
173          * @static\r
174          * @param       string  $tablename\r
175          * @return      void\r
176          *\r
177          */\r
178         static private function dump_structure($tablename)\r
179         {\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
185                 echo ";\n\n";\r
186                 return;\r
187         }\r
188 \r
189         /**\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
194          *\r
195          * @static\r
196          * @param       resource        $result\r
197          * @param       integer $num_fields\r
198          * @return      string\r
199          */\r
200         static private function get_field_names($result, $num_fields)\r
201         {\r
202                 $fields = array();\r
203                 for ( $j = 0; $j < $num_fields; $j++ )\r
204                 {\r
205                         $fields[] = sql_field_name($result, $j);\r
206                 }\r
207 \r
208                 return '(' . implode(', ', $fields) . ')';\r
209         }\r
210 \r
211         /**\r
212          * Backup::dump_contents()\r
213          * Creates a dump of the table content for one table\r
214          *\r
215          * @static\r
216          * @param       string  $tablename\r
217          * @return      void\r
218          *\r
219          */\r
220         static private function dump_contents($tablename)\r
221         {\r
222                 /*\r
223                  * Grab the data from the table.\r
224                 */\r
225                 $result = sql_query("SELECT * FROM $tablename");\r
226 \r
227                 if ( sql_num_rows($result) > 0 )\r
228                 {\r
229                         echo "\n";\r
230                         echo "/*\n";\r
231                         echo " * Table Data for {$tablename}\n";\r
232                         echo " */\n";\r
233                 }\r
234 \r
235                 $num_fields = sql_num_fields($result);\r
236 \r
237                 /*\r
238                  * Compose fieldname list\r
239                 */\r
240                 $tablename_list = self::get_field_names($result, $num_fields);\r
241 \r
242                 /*\r
243                  * Loop through the resulting rows and build the sql statement.\r
244                 */\r
245                 while ( $row = sql_fetch_array($result) )\r
246                 {\r
247                         // Start building the SQL statement.\r
248                         echo "INSERT INTO ".$tablename." $tablename_list VALUES(";\r
249 \r
250                         // Loop through the rows and fill in data for each column\r
251                         for ( $j = 0; $j < $num_fields; $j++ )\r
252                         {\r
253                                 if ( !isset($row[$j]) )\r
254                                 {\r
255                                         // no data for column\r
256                                         echo ' NULL';\r
257                                 }\r
258                                 elseif ( $row[$j] != '' )\r
259                                 {\r
260                                         // data\r
261                                         echo " '" . sql_real_escape_string($row[$j]) . "'";\r
262                                 }\r
263                                 else\r
264                                 {\r
265                                         // empty column (!= no data!)\r
266                                         echo "''";\r
267                                 }\r
268 \r
269                                 // only add comma when not last column\r
270                                 if ( $j != ($num_fields - 1) )\r
271                                 {\r
272                                         echo ",";\r
273                                 }\r
274                         }\r
275                         echo ");\n";\r
276                 }\r
277                 echo "\n";\r
278                 return;\r
279         }\r
280 \r
281         /**\r
282          * Backup::gzip_print_four_characters()\r
283          *\r
284          * @static\r
285          * @param       integer $val\r
286          * @return      integer\r
287          */\r
288         static private function gzip_print_four_characters($Val)\r
289         {\r
290                 for ( $i = 0; $i < 4; $i ++ )\r
291                 {\r
292                         $return .= chr($Val % 256);\r
293                         $Val = floor($Val / 256);\r
294                 }\r
295                 return $return;\r
296         }\r
297 \r
298         /**\r
299          * Backup::do_restore()\r
300          * Restores a database backup\r
301          *\r
302          * NOTE: this remains not-static for compatibility\r
303          *\r
304          * @param       void\r
305          * @return      void\r
306          */\r
307         public function do_restore()\r
308         {\r
309                 $uploadInfo = postFileInfo('backup_file');\r
310 \r
311                 // first of all: get uploaded file:\r
312                 if ( array_key_exists('name', $uploadInfo) && empty($uploadInfo['name']) )\r
313                 {\r
314                         return 'No file uploaded';\r
315                 }\r
316                 if ( !is_uploaded_file($uploadInfo['tmp_name']) )\r
317                 {\r
318                         return 'No file uploaded';\r
319                 }\r
320 \r
321                 $backup_file_name = $uploadInfo['name'];\r
322                 $backup_file_tmpname = $uploadInfo['tmp_name'];\r
323                 $backup_file_type = $uploadInfo['type'];\r
324 \r
325                 if ( !file_exists($backup_file_tmpname) )\r
326                 {\r
327                         return 'File Upload Error';\r
328                 }\r
329 \r
330                 if ( !preg_match("#^(text/[a-zA-Z]+)|(application/(x\-)?gzip(\-compressed)?)|(application/octet-stream)$#i", $backup_file_type) )\r
331                 {\r
332                         return 'The uploaded file is not of the correct type';\r
333                 }\r
334 \r
335                 $gzip = 0;\r
336                 if ( preg_match("#\.gz#i", $backup_file_name) )\r
337                 {\r
338                         $gzip = 1;\r
339                 }\r
340 \r
341                 if ( !extension_loaded("zlib") && $gzip )\r
342                 {\r
343                         return 'Cannot decompress gzipped backup (zlib package not installed)';\r
344                 }\r
345 \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
349                 {\r
350                         return 'Cannot get contents from this file.';\r
351                 }\r
352 \r
353                 /* detect lines */\r
354                 $lines = preg_split('/[\r\n]/', $contents);\r
355                 if( $lines === $contents )\r
356                 {\r
357                         return 'Cannot parse contents from this file';\r
358                 }\r
359 \r
360                 /* get sql statements from each lines */\r
361                 $queries = self::get_queries($lines);\r
362                 if ( $queries === array() )\r
363                 {\r
364                         return "Cannot get SQL queries from this file.";\r
365                 }\r
366 \r
367                 /* execute sql statements */\r
368                 foreach ( $queries as $query )\r
369                 {\r
370                         if ( !sql_query($query) )\r
371                         {\r
372                                 debug('SQL Error: ' . sql_error());\r
373                                 break;\r
374                         }\r
375                         continue;\r
376                 }\r
377                 return;\r
378         }\r
379 \r
380         static private function get_contents($temporary_name, $gzip = 0)\r
381         {\r
382                 $contents = '';\r
383                 if ( $gzip )\r
384                 {\r
385                         // decompress and read\r
386                         $gz_ptr = gzopen($temporary_name, 'rb');\r
387                         while ( !gzeof($gz_ptr) )\r
388                         {\r
389                                 $contents .= gzgets($gz_ptr, 100000);\r
390                         }\r
391                 }\r
392                 else\r
393                 {\r
394                         // just read\r
395                         $fsize = filesize($temporary_name);\r
396                         if ( $fsize > 0 )\r
397                         {\r
398                                 $contents = fread(fopen($temporary_name, 'r'), $fsize);\r
399                         }\r
400                 }\r
401                 return $contents;\r
402         }\r
403 \r
404         static private function get_queries($lines)\r
405         {\r
406                 $query = '';\r
407                 $queries = array();\r
408                 foreach ( $lines as $line )\r
409                 {\r
410                         $line = trim($line);\r
411                         if ( !$line || $line[0] == '#' || preg_match('#^[\s|/]?\*#', $line) )\r
412                         {\r
413                                 continue;\r
414                         }\r
415 \r
416                         if ( preg_match('/^(.*);$/', $line, $matches) === 0 )\r
417                         {\r
418                                 $query .= $line;\r
419                         }\r
420                         else\r
421                         {\r
422                                 $query .= $matches[1];\r
423                                 $queries[] = $query;\r
424                                 $query = '';\r
425                         }\r
426                         continue;\r
427                 }\r
428                 return $queries;\r
429         }\r
430 }