[ Index ]

PHP Cross Reference of Nucleus CMS 3.32

title

Body

[close]

/nucleus/libs/ -> backup.php (source)

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


Generated: Tue Feb 12 15:34:36 2008 Cross-referenced by PHPXref 0.7