OSDN Git Service

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