[ Index ]

PHP Cross Reference of Nucleus CMS 3.64

title

Body

[close]

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

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


Generated: Mon May 2 16:14:08 2011 Cross-referenced by PHPXref 0.7.1