00001 <?php 00002 /* 00003 * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/) 00004 * Copyright (C) 2002-2007 The Nucleus Group 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * (see nucleus/documentation/index.html#license for more info) 00011 */ 00033 function do_backup($gzip = 0) { 00034 global $manager; 00035 00036 // tables of which backup is needed 00037 $tables = array( 00038 sql_table('actionlog'), 00039 sql_table('ban'), 00040 sql_table('blog'), 00041 sql_table('comment'), 00042 sql_table('config'), 00043 sql_table('item'), 00044 sql_table('karma'), 00045 sql_table('member'), 00046 sql_table('skin'), 00047 sql_table('skin_desc'), 00048 sql_table('team'), 00049 sql_table('template'), 00050 sql_table('template_desc'), 00051 sql_table('plugin'), 00052 sql_table('plugin_event'), 00053 sql_table('plugin_option'), 00054 sql_table('plugin_option_desc'), 00055 sql_table('category'), 00056 sql_table('activation'), 00057 sql_table('tickets'), 00058 ); 00059 00060 // add tables that plugins want to backup to the list 00061 // catch all output generated by plugins 00062 ob_start(); 00063 $res = sql_query('SELECT pfile FROM '.sql_table('plugin')); 00064 while ($plugName = mysql_fetch_object($res)) { 00065 $plug =& $manager->getPlugin($plugName->pfile); 00066 if ($plug) $tables = array_merge($tables, (array) $plug->getTableList()); 00067 } 00068 ob_end_clean(); 00069 00070 // remove duplicates 00071 $tables = array_unique($tables); 00072 00073 // make sure browsers don't cache the backup 00074 header("Pragma: no-cache"); 00075 00076 // don't allow gzip compression when extension is not loaded 00077 if (($gzip != 0) && !extension_loaded("zlib")) 00078 $gzip = 0; 00079 00080 00081 00082 if ($gzip) { 00083 // use an output buffer 00084 @ob_start(); 00085 @ob_implicit_flush(0); 00086 00087 // set filename 00088 $filename = 'nucleus_db_backup_'.strftime("%Y%m%d", time()).".sql.gz"; 00089 } else { 00090 $filename = 'nucleus_db_backup_'.strftime("%Y%m%d", time()).".sql"; 00091 } 00092 00093 00094 // send headers that tell the browser a file is coming 00095 header("Content-Type: text/x-delimtext; name=\"$filename\""); 00096 header("Content-disposition: attachment; filename=$filename"); 00097 00098 // dump header 00099 echo "#\n"; 00100 echo "# This is a backup file generated by Nucleus \n"; 00101 echo "# http://www.nucleuscms.org/\n"; 00102 echo "#\n"; 00103 echo "# backup-date: " . gmdate("d-m-Y H:i:s", time()) . " GMT\n"; 00104 global $nucleus; 00105 echo "# Nucleus CMS version: " . $nucleus['version'] . "\n"; 00106 echo "#\n"; 00107 echo "# WARNING: Only try to restore on servers running the exact same version of Nucleus\n"; 00108 echo "#\n"; 00109 00110 // dump all tables 00111 reset($tables); 00112 array_walk($tables, '_backup_dump_table'); 00113 00114 if($gzip) 00115 { 00116 $Size = ob_get_length(); 00117 $Crc = crc32(ob_get_contents()); 00118 $contents = gzcompress(ob_get_contents()); 00119 ob_end_clean(); 00120 echo "\x1f\x8b\x08\x00\x00\x00\x00\x00".substr($contents, 0, strlen($contents) - 4).gzip_PrintFourChars($Crc).gzip_PrintFourChars($Size); 00121 } 00122 00123 exit; 00124 00125 } 00126 00127 00132 function _backup_dump_table($tablename, $key) { 00133 00134 echo "#\n"; 00135 echo "# TABLE: " . $tablename . "\n"; 00136 echo "#\n"; 00137 00138 // dump table structure 00139 _backup_dump_structure($tablename); 00140 00141 // dump table contents 00142 _backup_dump_contents($tablename); 00143 } 00144 00145 function _backup_dump_structure($tablename) { 00146 00147 // add command to drop table on restore 00148 echo "DROP TABLE IF EXISTS $tablename;\n"; 00149 echo "CREATE TABLE $tablename(\n"; 00150 00151 // 00152 // Ok lets grab the fields... 00153 // 00154 $result = mysql_query("SHOW FIELDS FROM $tablename"); 00155 $row = mysql_fetch_array($result); 00156 while ($row) { 00157 00158 echo ' ' . $row['Field'] . ' ' . $row['Type']; 00159 00160 if(isset($row['Default'])) 00161 echo ' DEFAULT \'' . $row['Default'] . '\''; 00162 00163 if($row['Null'] != "YES") 00164 echo ' NOT NULL'; 00165 00166 if($row['Extra'] != "") 00167 echo ' ' . $row['Extra']; 00168 00169 $row = mysql_fetch_array($result); 00170 00171 // add comma's except for last one 00172 if ($row) 00173 echo ",\n"; 00174 } 00175 00176 // 00177 // Get any Indexed fields from the database... 00178 // 00179 $result = mysql_query("SHOW KEYS FROM $tablename"); 00180 while($row = mysql_fetch_array($result)) { 00181 $kname = $row['Key_name']; 00182 00183 if(($kname != 'PRIMARY') && ($row['Non_unique'] == 0)) 00184 $kname = "UNIQUE|$kname"; 00185 if(($kname != 'PRIMARY') && ($row['Index_type'] == 'FULLTEXT')) 00186 $kname = "FULLTEXT|$kname"; 00187 00188 if(!is_array($index[$kname])) 00189 $index[$kname] = array(); 00190 00191 $index[$kname][] = $row['Column_name'] . ( ($row['Sub_part']) ? ' (' . $row['Sub_part'] . ')' : ''); 00192 } 00193 00194 while(list($x, $columns) = @each($index)) { 00195 echo ", \n"; 00196 00197 if($x == 'PRIMARY') 00198 echo ' PRIMARY KEY (' . implode($columns, ', ') . ')'; 00199 elseif (substr($x,0,6) == 'UNIQUE') 00200 echo ' UNIQUE KEY ' . substr($x,7) . ' (' . implode($columns, ', ') . ')'; 00201 elseif (substr($x,0,8) == 'FULLTEXT') 00202 echo ' FULLTEXT KEY ' . substr($x,9) . ' (' . implode($columns, ', ') . ')'; 00203 elseif (($x == 'ibody') || ($x == 'cbody')) // karma 2004-05-30 quick and dirty fix. fulltext keys were not in SQL correctly. 00204 echo ' FULLTEXT KEY ' . substr($x,9) . ' (' . implode($columns, ', ') . ')'; 00205 else 00206 echo " KEY $x (" . implode($columns, ', ') . ')'; 00207 } 00208 00209 echo "\n);\n\n"; 00210 } 00211 00218 function _backup_get_field_names($result, $num_fields) { 00219 00220 /* if (function_exists('mysqli_fetch_fields') ) { 00221 00222 $fields = mysqli_fetch_fields($result); 00223 for ($j = 0; $j < $num_fields; $j++) 00224 $fields[$j] = $fields[$j]->name; 00225 00226 } else {*/ 00227 00228 $fields = array(); 00229 for ($j = 0; $j < $num_fields; $j++) { 00230 $fields[] = mysql_field_name($result, $j); 00231 } 00232 00233 /* }*/ 00234 00235 return '(' . implode(', ', $fields) . ')'; 00236 } 00237 00238 function _backup_dump_contents($tablename) { 00239 // 00240 // Grab the data from the table. 00241 // 00242 $result = mysql_query("SELECT * FROM $tablename"); 00243 00244 if(mysql_num_rows($result) > 0) 00245 echo "\n#\n# Table Data for $tablename\n#\n"; 00246 00247 $num_fields = mysql_num_fields($result); 00248 00249 // 00250 // Compose fieldname list 00251 // 00252 $tablename_list = _backup_get_field_names($result, $num_fields); 00253 00254 // 00255 // Loop through the resulting rows and build the sql statement. 00256 // 00257 while ($row = mysql_fetch_array($result)) 00258 { 00259 // Start building the SQL statement. 00260 00261 echo "INSERT INTO $tablename $tablename_list VALUES("; 00262 00263 // Loop through the rows and fill in data for each column 00264 for ($j = 0; $j < $num_fields; $j++) { 00265 if(!isset($row[$j])) { 00266 // no data for column 00267 echo ' NULL'; 00268 } elseif ($row[$j] != '') { 00269 // data 00270 echo " '" . addslashes($row[$j]) . "'"; 00271 } else { 00272 // empty column (!= no data!) 00273 echo "''"; 00274 } 00275 00276 // only add comma when not last column 00277 if ($j != ($num_fields - 1)) 00278 echo ","; 00279 } 00280 00281 echo ");\n"; 00282 00283 } 00284 00285 00286 echo "\n"; 00287 00288 } 00289 00290 // copied from phpBB 00291 function gzip_PrintFourChars($Val) 00292 { 00293 for ($i = 0; $i < 4; $i ++) 00294 { 00295 $return .= chr($Val % 256); 00296 $Val = floor($Val / 256); 00297 } 00298 return $return; 00299 } 00300 00301 function do_restore() { 00302 00303 $uploadInfo = postFileInfo('backup_file'); 00304 00305 // first of all: get uploaded file: 00306 if (empty($uploadInfo['name'])) 00307 return 'No file uploaded'; 00308 if (!is_uploaded_file($uploadInfo['tmp_name'])) 00309 return 'No file uploaded'; 00310 00311 $backup_file_name = $uploadInfo['name']; 00312 $backup_file_tmpname = $uploadInfo['tmp_name']; 00313 $backup_file_type = $uploadInfo['type']; 00314 00315 if (!file_exists($backup_file_tmpname)) 00316 return 'File Upload Error'; 00317 00318 if (!preg_match("/^(text\/[a-zA-Z]+)|(application\/(x\-)?gzip(\-compressed)?)|(application\/octet-stream)$/is", $backup_file_type) ) 00319 return 'The uploaded file is not of the correct type'; 00320 00321 00322 00323 if (preg_match("/\.gz/is",$backup_file_name)) 00324 $gzip = 1; 00325 else 00326 $gzip = 0; 00327 00328 if (!extension_loaded("zlib") && $gzip) 00329 return "Cannot decompress gzipped backup (zlib package not installed)"; 00330 00331 // get sql query according to gzip setting (either decompress, or not) 00332 if($gzip) 00333 { 00334 // decompress and read 00335 $gz_ptr = gzopen($backup_file_tmpname, 'rb'); 00336 $sql_query = ""; 00337 while( !gzeof($gz_ptr) ) 00338 $sql_query .= gzgets($gz_ptr, 100000); 00339 } else { 00340 // just read 00341 $fsize = filesize($backup_file_tmpname); 00342 if ($fsize <= 0) 00343 $sql_query = ''; 00344 else 00345 $sql_query = fread(fopen($backup_file_tmpname, 'r'), $fsize); 00346 } 00347 00348 // time to execute the query 00349 _execute_queries($sql_query); 00350 } 00351 00352 function _execute_queries($sql_query) { 00353 if (!$sql_query) return; 00354 00355 // Strip out sql comments... 00356 $sql_query = remove_remarks($sql_query); 00357 $pieces = split_sql_file($sql_query); 00358 00359 $sql_count = count($pieces); 00360 for($i = 0; $i < $sql_count; $i++) 00361 { 00362 $sql = trim($pieces[$i]); 00363 00364 if(!empty($sql) and $sql[0] != "#") 00365 { 00366 // DEBUG 00367 // debug("Executing: " . htmlspecialchars($sql) . "\n"); 00368 00369 $result = mysql_query($sql); 00370 if (!$result) debug('SQL Error: ' . mysql_error()); 00371 00372 } 00373 } 00374 00375 } 00376 00377 // 00378 // remove_remarks will strip the sql comment lines out of an uploaded sql file 00379 // 00380 function remove_remarks($sql) 00381 { 00382 $lines = explode("\n", $sql); 00383 00384 // try to keep mem. use down 00385 $sql = ""; 00386 00387 $linecount = count($lines); 00388 $output = ""; 00389 00390 for ($i = 0; $i < $linecount; $i++) 00391 { 00392 if (($i != ($linecount - 1)) || (strlen($lines[$i]) > 0)) 00393 { 00394 if ($lines[$i][0] != "#") 00395 { 00396 $output .= $lines[$i] . "\n"; 00397 } 00398 else 00399 { 00400 $output .= "\n"; 00401 } 00402 // Trading a bit of speed for lower mem. use here. 00403 $lines[$i] = ""; 00404 } 00405 } 00406 00407 return $output; 00408 00409 } 00410 00411 00412 // 00413 // split_sql_file will split an uploaded sql file into single sql statements. 00414 // Note: expects trim() to have already been run on $sql. 00415 // 00416 // taken from phpBB 00417 // 00418 function split_sql_file($sql) 00419 { 00420 // Split up our string into "possible" SQL statements. 00421 $tokens = explode( ";", $sql); 00422 00423 // try to save mem. 00424 $sql = ""; 00425 $output = array(); 00426 00427 // we don't actually care about the matches preg gives us. 00428 $matches = array(); 00429 00430 // this is faster than calling count($tokens) every time thru the loop. 00431 $token_count = count($tokens); 00432 for ($i = 0; $i < $token_count; $i++) 00433 { 00434 // Don't wanna add an empty string as the last thing in the array. 00435 if (($i != ($token_count - 1)) || (strlen($tokens[$i] > 0))) 00436 { 00437 00438 // even number of quotes means a complete SQL statement 00439 if (_evenNumberOfQuotes($tokens[$i])) 00440 { 00441 $output[] = $tokens[$i]; 00442 $tokens[$i] = ""; // save memory. 00443 } 00444 else 00445 { 00446 // incomplete sql statement. keep adding tokens until we have a complete one. 00447 // $temp will hold what we have so far. 00448 $temp = $tokens[$i] . ";"; 00449 $tokens[$i] = ""; // save memory.. 00450 00451 // Do we have a complete statement yet? 00452 $complete_stmt = false; 00453 00454 for ($j = $i + 1; (!$complete_stmt && ($j < $token_count)); $j++) 00455 { 00456 // odd number of quotes means a completed statement 00457 // (in combination with the odd number we had already) 00458 if (!_evenNumberOfQuotes($tokens[$j])) 00459 { 00460 $output[] = $temp . $tokens[$j]; 00461 00462 // save memory. 00463 $tokens[$j] = ""; 00464 $temp = ""; 00465 00466 // exit the loop. 00467 $complete_stmt = true; 00468 // make sure the outer loop continues at the right point. 00469 $i = $j; 00470 } 00471 else 00472 { 00473 // even number of unescaped quotes. We still don't have a complete statement. 00474 // (1 odd and 1 even always make an odd) 00475 $temp .= $tokens[$j] . ";"; 00476 // save memory. 00477 $tokens[$j] = ""; 00478 } 00479 00480 } // for.. 00481 } // else 00482 } 00483 } 00484 00485 return $output; 00486 } 00487 00488 00489 function _evenNumberOfQuotes($text) { 00490 // This is the total number of single quotes in the token. 00491 $total_quotes = preg_match_all("/'/", $text, $matches); 00492 // Counts single quotes that are preceded by an odd number of backslashes, 00493 // which means they're escaped quotes. 00494 $escaped_quotes = preg_match_all("/(?<!\\\\)(\\\\\\\\)*\\\\'/", $text, $matches); 00495 00496 $unescaped_quotes = $total_quotes - $escaped_quotes; 00497 // debug($total_quotes . "-" . $escaped_quotes . "-" . $unescaped_quotes); 00498 return (($unescaped_quotes % 2) == 0); 00499 } 00500 00501 ?>