OSDN Git Service

sync the original code
[nucleus-jp/nucleus-jp-ancient.git] / utf8 / nucleus / libs / backup.php
1 <?php\r
2 /*\r
3  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)\r
4  * Copyright (C) 2002-2005 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  * Based on code in phpBB (http://phpBB.sourceforge.net)\r
16  *\r
17  * @license http://nucleuscms.org/license.txt GNU General Public License\r
18  * @copyright Copyright (C) 2002-2005 The Nucleus Group\r
19  * @version $Id: backup.php,v 1.4 2005-08-13 07:31:04 kimitake Exp $\r
20  * $NucleusJP: backup.php,v 1.3 2005/03/12 06:19:05 kimitake Exp $\r
21  */\r
22 \r
23  \r
24 /**\r
25   * This function creates an sql dump of the database and sends it to\r
26   * the user as a file (can be gzipped if they want)\r
27   *\r
28   * @requires \r
29   *             no output may have preceded (new headers are sent)\r
30   * @param gzip \r
31   *             1 = compress backup file, 0 = no compression (default)\r
32   */\r
33 function do_backup($gzip = 0) {\r
34         global $manager;\r
35 \r
36         // tables of which backup is needed\r
37         $tables = array(\r
38                                         sql_table('actionlog'),\r
39                                         sql_table('ban'),\r
40                                         sql_table('blog'),\r
41                                         sql_table('comment'),\r
42                                         sql_table('config'),\r
43                                         sql_table('item'),\r
44                                         sql_table('karma'),\r
45                                         sql_table('member'),\r
46                                         sql_table('skin'),\r
47                                         sql_table('skin_desc'),\r
48                                         sql_table('team'),\r
49                                         sql_table('template'),\r
50                                         sql_table('template_desc'),\r
51                                         sql_table('plugin'),\r
52                                         sql_table('plugin_event'),\r
53                                         sql_table('plugin_option'),\r
54                                         sql_table('plugin_option_desc'),\r
55                                         sql_table('category'),\r
56                                         sql_table('activation'),\r
57                                         sql_table('tickets'),\r
58                           );\r
59 \r
60         // add tables that plugins want to backup to the list\r
61         // catch all output generated by plugins\r
62         ob_start();\r
63         $res = sql_query('SELECT pfile FROM '.sql_table('plugin'));\r
64         while ($plugName = mysql_fetch_object($res)) {\r
65                 $plug =& $manager->getPlugin($plugName->pfile);\r
66                 if ($plug) $tables = array_merge($tables, $plug->getTableList());\r
67         }\r
68         ob_end_clean();\r
69                 \r
70         // remove duplicates\r
71         $tables = array_unique($tables);\r
72                           \r
73         // make sure browsers don't cache the backup\r
74         header("Pragma: no-cache");\r
75         \r
76         // don't allow gzip compression when extension is not loaded\r
77         if (($gzip != 0) && !extension_loaded("zlib"))\r
78                 $gzip = 0;\r
79                 \r
80         \r
81 \r
82         if ($gzip) {\r
83                 // use an output buffer\r
84                 @ob_start();\r
85                 @ob_implicit_flush(0);\r
86                 \r
87                 // set filename\r
88                 $filename = 'nucleus_db_backup_'.strftime("%Y%m%d", time()).".sql.gz";\r
89         } else {\r
90                 $filename = 'nucleus_db_backup_'.strftime("%Y%m%d", time()).".sql";\r
91         }\r
92         \r
93         \r
94         // send headers that tell the browser a file is coming\r
95         header("Content-Type: text/x-delimtext; name=\"$filename\"");\r
96         header("Content-disposition: attachment; filename=$filename");\r
97                 \r
98         // dump header\r
99         echo "#\n";\r
100         echo "# This is a backup file generated by Nucleus \n";\r
101         echo "# http://www.nucleuscms.org/\n";\r
102         echo "#\n";\r
103         echo "# backup-date: " .  gmdate("d-m-Y H:i:s", time()) . " GMT\n";\r
104         global $nucleus;\r
105         echo "# Nucleus CMS version: " . $nucleus['version'] . "\n";    \r
106         echo "#\n";\r
107         echo "# WARNING: Only try to restore on servers running the exact same version of Nucleus\n";\r
108         echo "#\n";\r
109         \r
110         // dump all tables\r
111         reset($tables);\r
112         array_walk($tables, '_backup_dump_table');\r
113         \r
114         if($gzip)\r
115         {\r
116                 $Size = ob_get_length();\r
117                 $Crc = crc32(ob_get_contents());\r
118                 $contents = gzcompress(ob_get_contents());\r
119                 ob_end_clean();\r
120                 echo "\x1f\x8b\x08\x00\x00\x00\x00\x00".substr($contents, 0, strlen($contents) - 4).gzip_PrintFourChars($Crc).gzip_PrintFourChars($Size);\r
121         }\r
122         \r
123         exit;\r
124 \r
125 }\r
126 \r
127 \r
128 /**\r
129   * Creates a dump for a single table\r
130   * ($tablename and $key are filled in by array_walk)\r
131   */\r
132 function _backup_dump_table($tablename, $key) {\r
133 \r
134         echo "#\n";\r
135         echo "# TABLE: " . $tablename . "\n";\r
136         echo "#\n";\r
137         \r
138         // dump table structure\r
139         _backup_dump_structure($tablename);\r
140 \r
141         // dump table contents\r
142         _backup_dump_contents($tablename);\r
143 }\r
144 \r
145 function _backup_dump_structure($tablename) {\r
146         \r
147         // add command to drop table on restore\r
148         echo "DROP TABLE IF EXISTS $tablename;\n";\r
149         echo "CREATE TABLE $tablename(\n";\r
150 \r
151         //\r
152         // Ok lets grab the fields...\r
153         //\r
154         $result = mysql_query("SHOW FIELDS FROM $tablename");\r
155         $row = mysql_fetch_array($result);\r
156         while ($row) {\r
157 \r
158                 echo '  ' . $row['Field'] . ' ' . $row['Type'];\r
159 \r
160                 if(!empty($row['Default']))\r
161                         echo ' DEFAULT \'' . $row['Default'] . '\'';\r
162 \r
163                 if($row['Null'] != "YES")\r
164                         echo ' NOT NULL';\r
165 \r
166                 if($row['Extra'] != "")\r
167                         echo ' ' . $row['Extra'];\r
168 \r
169                 $row = mysql_fetch_array($result);\r
170 \r
171                 // add comma's except for last one\r
172                 if ($row)\r
173                         echo ",\n";\r
174         }\r
175 \r
176         //\r
177         // Get any Indexed fields from the database...\r
178         //\r
179         $result = mysql_query("SHOW KEYS FROM $tablename");\r
180         while($row = mysql_fetch_array($result)) {\r
181                 $kname = $row['Key_name'];\r
182 \r
183                 if(($kname != 'PRIMARY') && ($row['Non_unique'] == 0))\r
184                         $kname = "UNIQUE|$kname";\r
185                 if(($kname != 'PRIMARY') && ($row['Index_type'] == 'FULLTEXT'))\r
186                         $kname = "FULLTEXT|$kname";\r
187                         \r
188                 if(!is_array($index[$kname]))\r
189                         $index[$kname] = array();\r
190         \r
191                 $index[$kname][] = $row['Column_name'];\r
192         }\r
193 \r
194         while(list($x, $columns) = @each($index)) {\r
195                 echo ", \n";\r
196 \r
197                 if($x == 'PRIMARY')\r
198                         echo '  PRIMARY KEY (' . implode($columns, ', ') . ')';\r
199                 elseif (substr($x,0,6) == 'UNIQUE')\r
200                         echo '  UNIQUE KEY ' . substr($x,7) . ' (' . implode($columns, ', ') . ')';\r
201                 elseif (substr($x,0,8) == 'FULLTEXT')\r
202                         echo '  FULLTEXT KEY ' . substr($x,9) . ' (' . implode($columns, ', ') . ')';\r
203                 elseif (($x == 'ibody') || ($x == 'cbody'))                     // karma 2004-05-30 quick and dirty fix. fulltext keys were not in SQL correctly.\r
204                         echo '  FULLTEXT KEY ' . substr($x,9) . ' (' . implode($columns, ', ') . ')';                   \r
205                 else \r
206                         echo "  KEY $x (" . implode($columns, ', ') . ')';\r
207         }\r
208 \r
209         echo "\n);\n\n";\r
210 }\r
211 \r
212 function _backup_dump_contents($tablename) {\r
213         //\r
214         // Grab the data from the table.\r
215         //\r
216         $result = mysql_query("SELECT * FROM $tablename");\r
217 \r
218         if(mysql_numrows($result) > 0)\r
219                 echo "\n#\n# Table Data for $tablename\n#\n";\r
220 \r
221         //\r
222         // Loop through the resulting rows and build the sql statement.\r
223         //\r
224         while ($row = mysql_fetch_array($result))\r
225         {\r
226                 $tablename_list = '(';\r
227                 $num_fields = mysql_num_fields($result);\r
228 \r
229                 //\r
230                 // Grab the list of field names.\r
231                 //\r
232                 for ($j = 0; $j < $num_fields; $j++)\r
233                         $tablename_list .= mysql_field_name($result, $j) . ', ';\r
234 \r
235                 //\r
236                 // Get rid of the last comma\r
237                 //\r
238                 $tablename_list = ereg_replace(', $', '', $tablename_list);\r
239                 $tablename_list .= ')';\r
240 \r
241                 // Start building the SQL statement.\r
242 \r
243                 echo "INSERT INTO $tablename $tablename_list VALUES(";\r
244 \r
245                 // Loop through the rows and fill in data for each column\r
246                 for ($j = 0; $j < $num_fields; $j++) {\r
247                         if(!isset($row[$j])) {\r
248                                 // no data for column\r
249                                 echo ' NULL';\r
250                         } elseif ($row[$j] != '') {\r
251                                 // data\r
252                                 echo " '" . addslashes($row[$j]) . "'";\r
253                         } else {\r
254                                 // empty column (!= no data!)\r
255                                 echo "''";\r
256                         }\r
257 \r
258                         // only add comma when not last column\r
259                         if ($j != ($num_fields - 1))\r
260                                 echo ",";\r
261                 }\r
262 \r
263                 echo ");\n";\r
264 \r
265         }\r
266         \r
267         \r
268         echo "\n";\r
269 \r
270 }\r
271 \r
272 // copied from phpBB\r
273 function gzip_PrintFourChars($Val)\r
274 {\r
275         for ($i = 0; $i < 4; $i ++)\r
276         {\r
277                 $return .= chr($Val % 256);\r
278                 $Val = floor($Val / 256);\r
279         }\r
280         return $return;\r
281\r
282 \r
283 function do_restore() {\r
284         \r
285         $uploadInfo = postFileInfo('backup_file');\r
286         \r
287         // first of all: get uploaded file:\r
288         if (empty($uploadInfo['name']))\r
289                 return 'No file uploaded';\r
290         if (!is_uploaded_file($uploadInfo['tmp_name']))\r
291                 return 'No file uploaded';\r
292                 \r
293         $backup_file_name = $uploadInfo['name'];\r
294         $backup_file_tmpname = $uploadInfo['tmp_name'];\r
295         $backup_file_type = $uploadInfo['type'];\r
296 \r
297         if (!file_exists($backup_file_tmpname))\r
298                 return 'File Upload Error';\r
299         \r
300         if (!preg_match("/^(text\/[a-zA-Z]+)|(application\/(x\-)?gzip(\-compressed)?)|(application\/octet-stream)$/is", $backup_file_type) )\r
301                 return 'The uploaded file is not of the correct type';\r
302                 \r
303                 \r
304         \r
305         if (preg_match("/\.gz/is",$backup_file_name)) \r
306                 $gzip = 1;\r
307         else\r
308                 $gzip = 0;\r
309                 \r
310         if (!extension_loaded("zlib") && $gzip)\r
311                 return "Cannot decompress gzipped backup (zlib package not installed)";\r
312 \r
313         // get sql query according to gzip setting (either decompress, or not)\r
314         if($gzip)\r
315         {\r
316                 // decompress and read\r
317                 $gz_ptr = gzopen($backup_file_tmpname, 'rb');\r
318                 $sql_query = "";\r
319                 while( !gzeof($gz_ptr) )\r
320                         $sql_query .= gzgets($gz_ptr, 100000);\r
321         } else {\r
322                 // just read\r
323                 $fsize = filesize($backup_file_tmpname);\r
324                 if ($fsize <= 0)\r
325                         $sql_query = '';\r
326                 else\r
327                         $sql_query = fread(fopen($backup_file_tmpname, 'r'), $fsize);\r
328         }\r
329 \r
330         // time to execute the query\r
331         _execute_queries($sql_query);\r
332 }\r
333 \r
334 function _execute_queries($sql_query) {\r
335         if (!$sql_query) return;\r
336 \r
337         // Strip out sql comments...\r
338         $sql_query = remove_remarks($sql_query);\r
339         $pieces = split_sql_file($sql_query);\r
340 \r
341         $sql_count = count($pieces);\r
342         for($i = 0; $i < $sql_count; $i++)\r
343         {\r
344                 $sql = trim($pieces[$i]);\r
345 \r
346                 if(!empty($sql) and $sql[0] != "#")\r
347                 {\r
348                         // DEBUG\r
349 //                      debug("Executing: " . htmlspecialchars($sql) . "\n");\r
350 \r
351                         $result = mysql_query($sql);\r
352                         if (!$result) debug("SQL Error: " + mysql_error());\r
353 \r
354                 }\r
355         }\r
356 \r
357 }\r
358 \r
359 //\r
360 // remove_remarks will strip the sql comment lines out of an uploaded sql file\r
361 //\r
362 function remove_remarks($sql)\r
363 {\r
364         $lines = explode("\n", $sql);\r
365         \r
366         // try to keep mem. use down\r
367         $sql = "";\r
368         \r
369         $linecount = count($lines);\r
370         $output = "";\r
371 \r
372         for ($i = 0; $i < $linecount; $i++)\r
373         {\r
374                 if (($i != ($linecount - 1)) || (strlen($lines[$i]) > 0))\r
375                 {\r
376                         if ($lines[$i][0] != "#")\r
377                         {\r
378                                 $output .= $lines[$i] . "\n";\r
379                         }\r
380                         else\r
381                         {\r
382                                 $output .= "\n";\r
383                         }\r
384                         // Trading a bit of speed for lower mem. use here.\r
385                         $lines[$i] = "";\r
386                 }\r
387         }\r
388         \r
389         return $output;\r
390         \r
391 }\r
392 \r
393 \r
394 //\r
395 // split_sql_file will split an uploaded sql file into single sql statements.\r
396 // Note: expects trim() to have already been run on $sql.\r
397 //\r
398 // taken from phpBB\r
399 //\r
400 function split_sql_file($sql)\r
401 {\r
402         // Split up our string into "possible" SQL statements.\r
403         $tokens = explode( ";", $sql);\r
404 \r
405         // try to save mem.\r
406         $sql = "";\r
407         $output = array();\r
408         \r
409         // we don't actually care about the matches preg gives us.\r
410         $matches = array();\r
411         \r
412         // this is faster than calling count($tokens) every time thru the loop.\r
413         $token_count = count($tokens);\r
414         for ($i = 0; $i < $token_count; $i++)\r
415         {\r
416                 // Don't wanna add an empty string as the last thing in the array.\r
417                 if (($i != ($token_count - 1)) || (strlen($tokens[$i] > 0)))\r
418                 {\r
419                         \r
420                         // even number of quotes means a complete SQL statement\r
421                         if (_evenNumberOfQuotes($tokens[$i]))\r
422                         {\r
423                                 $output[] = $tokens[$i];\r
424                                 $tokens[$i] = "";       // save memory.\r
425                         }\r
426                         else\r
427                         {\r
428                                 // incomplete sql statement. keep adding tokens until we have a complete one.\r
429                                 // $temp will hold what we have so far.\r
430                                 $temp = $tokens[$i] .  ";";\r
431                                 $tokens[$i] = "";       // save memory..\r
432                                 \r
433                                 // Do we have a complete statement yet? \r
434                                 $complete_stmt = false;\r
435                                 \r
436                                 for ($j = $i + 1; (!$complete_stmt && ($j < $token_count)); $j++)\r
437                                 {\r
438                                         // odd number of quotes means a completed statement \r
439                                         // (in combination with the odd number we had already)\r
440                                         if (!_evenNumberOfQuotes($tokens[$j]))\r
441                                         {\r
442                                                 $output[] = $temp . $tokens[$j];\r
443 \r
444                                                 // save memory.\r
445                                                 $tokens[$j] = "";\r
446                                                 $temp = "";\r
447                                                 \r
448                                                 // exit the loop.\r
449                                                 $complete_stmt = true;\r
450                                                 // make sure the outer loop continues at the right point.\r
451                                                 $i = $j;\r
452                                         }\r
453                                         else\r
454                                         {\r
455                                                 // even number of unescaped quotes. We still don't have a complete statement. \r
456                                                 // (1 odd and 1 even always make an odd)\r
457                                                 $temp .= $tokens[$j] .  ";";\r
458                                                 // save memory.\r
459                                                 $tokens[$j] = "";\r
460                                         }\r
461                                         \r
462                                 } // for..\r
463                         } // else\r
464                 }\r
465         }\r
466 \r
467         return $output;\r
468 }\r
469 \r
470 \r
471 function _evenNumberOfQuotes($text) {\r
472                 // This is the total number of single quotes in the token.\r
473                 $total_quotes = preg_match_all("/'/", $text, $matches);\r
474                 // Counts single quotes that are preceded by an odd number of backslashes, \r
475                 // which means they're escaped quotes.\r
476                 $escaped_quotes = preg_match_all("/(?<!\\\\)(\\\\\\\\)*\\\\'/", $text, $matches);\r
477 \r
478                 $unescaped_quotes = $total_quotes - $escaped_quotes;\r
479 //              debug($total_quotes . "-" . $escaped_quotes . "-" . $unescaped_quotes);\r
480                 return (($unescaped_quotes % 2) == 0);\r
481 }\r
482 \r
483 ?>\r