OSDN Git Service

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