[ Index ] |
|
Code source de eGroupWare 1.2.106-2 |
1 <?php 2 /**************************************************************************\ 3 * eGroupWare - Setup - db-backups * 4 * http://www.egroupware.org * 5 * -------------------------------------------- * 6 * Written and copyright by Ralf Becker <RalfBecker@outdoor-training.de> * 7 * -------------------------------------------- * 8 * This program is free software; you can redistribute it and/or modify it * 9 * under the terms of the GNU General Public License as published by the * 10 * Free Software Foundation; either version 2 of the License, or (at your * 11 * option) any later version. * 12 \**************************************************************************/ 13 14 /* $Id: class.db_backup.inc.php 20295 2006-02-15 12:31:25Z $ */ 15 16 define('BACKSLASH_TOKEN','##!!**bAcKsLaSh**!!##'); // used in cvs_split 17 18 /** 19 * DB independent backup and restore of eGW's DB 20 * 21 * @package phpgwapi 22 * @subpackage db 23 * @author RalfBecker-AT-outdoor-training.de 24 * @license GPL 25 */ 26 27 class db_backup 28 { 29 var $schema_proc; /** schema_proc class */ 30 var $schemas = array(); /** array tablename => schema */ 31 var $exclude_tables = array( /** exclude from backup */ 32 'egw_sessions','egw_app_sessions','phpgw_sessions','phpgw_app_sessions', // eGW's session-tables 33 'phpgw_anglemail', // email's cache 34 'egw_felamimail_cache','egw_felamimail_folderstatus','phpgw_felamimail_cache','phpgw_felamimail_folderstatus', // felamimail's cache 35 ); 36 var $system_tables = false; /** regular expression to identify system-tables => ignored for schema+backup */ 37 var $egw_tables = false; /** regurar expression to identify eGW tables => if set only they are used */ 38 39 /** 40 * Constructor 41 */ 42 function db_backup() 43 { 44 if (is_object($GLOBALS['egw_setup']->oProc)) // schema_proc already instanciated, use it 45 { 46 $this->schema_proc = $GLOBALS['egw_setup']->oProc; 47 } 48 else 49 { 50 $this->schema_proc = CreateObject('phpgwapi.schema_proc'); 51 } 52 53 $this->db = $this->schema_proc->m_odb; 54 $this->adodb = &$GLOBALS['egw']->ADOdb; 55 if (is_object($GLOBALS['egw_setup'])) // called from setup 56 { 57 if ($GLOBALS['egw_setup']->config_table && $GLOBALS['egw_setup']->table_exist(array($GLOBALS['egw_setup']->config_table))) 58 { 59 $this->db->query("SELECT config_value FROM {$GLOBALS['egw_setup']->config_table} WHERE config_app='phpgwapi' AND config_name='backup_dir'",__LINE__,__FILE__); 60 $this->db->next_record(); 61 if (!($this->backup_dir = $this->db->f(0))) 62 { 63 $this->db->query("SELECT config_value FROM {$GLOBALS['egw_setup']->config_table} WHERE config_app='phpgwapi' AND config_name='files_dir'",__LINE__,__FILE__); 64 $this->db->next_record(); 65 $this->backup_dir = $this->db->f(0).'/db_backup'; 66 } 67 $this->db->query("SELECT config_value FROM {$GLOBALS['egw_setup']->config_table} WHERE config_app='phpgwapi' AND config_name='system_charset'",__LINE__,__FILE__); 68 $this->db->next_record(); 69 $this->charset = $this->db->f(0); 70 if (!$this->charset) 71 { 72 $this->db->query("SELECT content FROM {$GLOBALS['egw_setup']->lang_table} WHERE message_id='charset' AND app_name='common' AND lang!='en'",__LINE__,__FILE__); 73 $this->db->next_record(); 74 $this->charset = $this->db->f(0); 75 } 76 $this->db->select($GLOBALS['egw_setup']->applications_table,'app_version',array('app_name'=>'phpgwapi'),__LINE__,__FILE__); 77 $this->api_version = $this->db->next_record() ? $this->db->f(0) : false; 78 } 79 if (!$this->charset) $this->charset = 'iso-8859-1'; 80 } 81 else // called from eGW 82 { 83 $this->schema_proc = CreateObject('phpgwapi.schema_proc'); 84 if (!($this->backup_dir = $GLOBALS['egw_info']['server']['backup_dir'])) 85 { 86 $this->backup_dir = $GLOBALS['egw_info']['server']['files_dir'].'/db_backup'; 87 } 88 $this->charset = $GLOBALS['egw']->translation->charset(); 89 90 $this->api_version = $GLOBALS['egw_info']['apps']['phpgwapi']['version']; 91 } 92 if (!is_dir($this->backup_dir) && is_writable(dirname($this->backup_dir))) 93 { 94 mkdir($this->backup_dir); 95 } 96 switch($this->db->Type) 97 { 98 case 'sapdb': 99 case 'maxdb': 100 //$this->system_tables = '/^(sql_cursor.*|session_roles|activeconfiguration|cachestatistics|commandcachestatistics|commandstatistics|datastatistics|datavolumes|hotstandbycomponent|hotstandbygroup|instance|logvolumes|machineconfiguration|machineutilization|memoryallocatorstatistics|memoryholders|omslocks|optimizerinformation|sessions|snapshots|spinlockstatistics|version)$/i'; 101 $this->egw_tables = '/^(egw_|phpgw_)/i'; 102 break; 103 } 104 } 105 106 /** 107 * Opens the backup-file using the highest availible compression 108 * 109 * @param $name=false string/boolean filename to use, or false for the default one 110 * @param $reading=false opening for reading ('rb') or writing ('wb') 111 * @return string/resource error-msg of file-handle 112 */ 113 function fopen_backup($name=false,$reading=false) 114 { 115 if (!$name) 116 { 117 if (!$this->backup_dir || !is_writable($this->backup_dir)) 118 { 119 return lang("backupdir '%1' is not writeable by the webserver",$this->backup_dir); 120 } 121 $name = $this->backup_dir.'/db_backup-'.date('YmdHi'); 122 } 123 else // remove the extension, to use the correct wrapper based on the extension 124 { 125 $name = preg_replace('/\.(bz2|gz)$/i','',$name); 126 } 127 $mode = $reading ? 'rb' : 'wb'; 128 129 if (!($f = @fopen($file = "compress.bzip2://$name.bz2",$mode)) && 130 !($f = @fopen($file = "compress.zlib://$name.gz",$mode)) && 131 !($f = @fopen($file = "zlib:$name.gz",$mode)) && // php < 4.3 132 !($f = @fopen($file = $name,$mode))) 133 { 134 $lang_mode = $reading ? lang('reading') : lang('writing'); 135 return lang("Cant open '%1' for %2",$name,$lang_mode); 136 } 137 return $f; 138 } 139 140 /** 141 * Backup all data in the form of a (compressed) csv file 142 * 143 * @param resource $f file opened with fopen for reading 144 * @param boolean $convert_to_system_charset=false convert the restored data to the selected system-charset 145 */ 146 function restore($f,$convert_to_system_charset=false) 147 { 148 @set_time_limit(0); 149 ini_set('auto_detect_line_endings',true); 150 151 $this->db->transaction_begin(); 152 153 // drop all existing tables 154 foreach($this->adodb->MetaTables('TABLES') as $table) 155 { 156 if ($this->system_tables && preg_match($this->system_tables,$table) || 157 $this->egw_tables && !preg_match($this->egw_tables,$table)) 158 { 159 continue; 160 } 161 $this->schema_proc->DropTable($table); 162 } 163 164 $table = False; 165 $n = 0; 166 while(!feof($f)) 167 { 168 $line = trim(fgets($f)); ++$n; 169 170 if (empty($line)) continue; 171 172 if (substr($line,0,9) == 'version: ') 173 { 174 $api_version = trim(substr($line,9)); 175 continue; 176 } 177 if (substr($line,0,9) == 'charset: ') 178 { 179 $charset = trim(substr($line,9)); 180 // needed if mbstring.func_overload > 0, else eg. substr does not work with non ascii chars 181 @ini_set('mbstring.internal_encoding',$charset); 182 183 // set the DB's client encoding (for mysql only if api_version >= 1.0.1.019) 184 if ((!$convert_to_system_charset || $this->db->capabilities['client_encoding']) && 185 (substr($this->db->Type,0,5) != 'mysql' || !is_object($GLOBALS['egw_setup']) || 186 $api_version && !$GLOBALS['egw_setup']->alessthanb($api_version,'1.0.1.019'))) 187 { 188 $this->db->Link_ID->SetCharSet($charset); 189 if (!$convert_to_system_charset) 190 { 191 $this->schema_proc->system_charset = $charset; // so schema_proc uses it for the creation of the tables 192 } 193 } 194 continue; 195 } 196 if (substr($line,0,8) == 'schema: ') 197 { 198 // create the tables in the backup set 199 $this->schemas = unserialize(trim(substr($line,8))); 200 foreach($this->schemas as $table_name => $schema) 201 { 202 echo "<pre>$table_name => ".$this->write_array($schema,1)."</pre>\n"; 203 $this->schema_proc->CreateTable($table_name,$schema); 204 } 205 // make the schemas availible for the db-class 206 $GLOBALS['egw_info']['apps']['all-apps']['table_defs'] = &$this->schemas; 207 continue; 208 } 209 if (substr($line,0,7) == 'table: ') 210 { 211 $table = substr($line,7); 212 213 $cols = $this->csv_split($line=fgets($f)); ++$n; 214 215 if (feof($f)) break; 216 continue; 217 } 218 if ($convert_to_system_charset && !$this->db->capabilities['client_encoding']) 219 { 220 if ($GLOBALS['egw_setup']) 221 { 222 if (!is_object($GLOBALS['egw_setup']->translation->sql)) 223 { 224 $GLOBALS['egw_setup']->translation->setup_translation_sql(); 225 } 226 $translation =& $GLOBALS['egw_setup']->translation->sql; 227 } 228 else 229 { 230 $translation =& $GLOBALS['egw']->translation; 231 } 232 } 233 if ($table) // do we already reached the data part 234 { 235 $data = $this->csv_split($line,$cols); 236 237 if (count($data) == count($cols)) 238 { 239 if ($convert_to_system_charset && !$this->db->capabilities['client_encoding']) 240 { 241 $translation->convert($data,$charset); 242 } 243 $this->db->insert($table,$data,False,__LINE__,__FILE__,'all-apps',true); 244 } 245 else 246 { 247 echo '<p>'.lang("Line %1: '%2'<br><b>csv data does not match column-count of table %3 ==> ignored</b>",$n,$line,$table)."</p>\n"; 248 echo 'data=<pre>'.print_r($data,true)."</pre>\n"; 249 } 250 } 251 } 252 // updated the sequences, if the DB uses them 253 foreach($this->schemas as $table => $schema) 254 { 255 foreach($schema['fd'] as $column => $definition) 256 { 257 if ($definition['type'] == 'auto') 258 { 259 $this->schema_proc->UpdateSequence($table,$column); 260 break; // max. one per table 261 } 262 } 263 } 264 265 if ($convert_to_system_charset) // store the changed charset 266 { 267 $this->db->insert($GLOBALS['egw_setup']->config_table,array( 268 'config_value' => $GLOBALS['egw_setup']->system_charset, 269 ),array( 270 'config_app' => 'phpgwapi', 271 'config_name' => 'system_charset', 272 ),__LINE__,__FILE__); 273 } 274 $this->db->transaction_commit(); 275 } 276 277 /** 278 * Split one line of a csv file into an array and does all unescaping 279 * @internal 280 */ 281 function csv_split($line,$keys=False) 282 { 283 $fields = explode(',',trim($line)); 284 285 $str_pending = False; 286 $n = 0; 287 foreach($fields as $i => $field) 288 { 289 if ($str_pending !== False) 290 { 291 $field = $str_pending.','.$field; 292 $str_pending = False; 293 } 294 $key = $keys ? $keys[$n] : $n; 295 296 if ($field[0] == '"') 297 { 298 if (substr($field,-1) !== '"' || $field === '"' || !preg_match('/[^\\\\]+(\\\\\\\\)*"$/',$field)) 299 { 300 $str_pending = $field; 301 continue; 302 } 303 $arr[$key] = str_replace(BACKSLASH_TOKEN,'\\',str_replace(array('\\\\','\\n','\\r','\\"'),array(BACKSLASH_TOKEN,"\n","\r",'"'),substr($field,1,-1))); 304 } 305 else 306 { 307 $arr[$key] = $field == 'NULL' ? NULL : $field; 308 } 309 ++$n; 310 } 311 return $arr; 312 } 313 314 /** 315 * @internal 316 */ 317 function escape_data(&$data,$col,$defs) 318 { 319 if (is_null($data)) 320 { 321 $data = 'NULL'; 322 } 323 else 324 { 325 switch($defs[$col]['type']) 326 { 327 case 'int': 328 case 'auto': 329 case 'decimal': 330 case 'date': 331 case 'timestamp': 332 break; 333 default: 334 $data = '"'.str_replace(array('\\',"\n","\r",'"'),array('\\\\','\\n','\\r','\\"'),$data).'"'; 335 break; 336 } 337 } 338 } 339 340 /** 341 * Backup all data in the form of a (compressed) csv file 342 * 343 * @param f resource file opened with fopen for writing 344 */ 345 function backup($f) 346 { 347 @set_time_limit(0); 348 349 fwrite($f,"eGroupWare backup from ".date('Y-m-d H:i:s')."\n\n"); 350 351 fwrite($f,"version: $this->api_version\n\n"); 352 353 fwrite($f,"charset: $this->charset\n\n"); 354 355 $this->schema_backup($f); // add the schema in a human readable form too 356 357 /* not needed, already done by schema_backup 358 foreach($this->adodb->MetaTables('TABLES') as $table) 359 { 360 if ($this->db->Type == 'sapdb' || $this->db->Type == 'maxdb') 361 { 362 $table = strtolower($table); 363 } 364 if (!($this->schemas[$table] = $this->schema_proc->GetTableDefinition($table))) 365 { 366 unset($this->schemas[$table]); 367 } 368 } 369 */ 370 fwrite($f,"\nschema: ".serialize($this->schemas)."\n"); 371 372 foreach($this->schemas as $table => $schema) 373 { 374 if (in_array($table,$this->exclude_tables)) continue; // dont backup 375 376 $first_row = true; 377 $this->db->select($table,'*',false,__LINE__,__FILE__); 378 while(($row = $this->db->row(true))) 379 { 380 if ($first_row) 381 { 382 fwrite($f,"\ntable: $table\n".implode(',',array_keys($row))."\n"); 383 $first_row = false; 384 } 385 array_walk($row,array('db_backup','escape_data'),$schema['fd']); 386 fwrite($f,implode(',',$row)."\n"); 387 } 388 } 389 return true; 390 } 391 392 /** 393 * Backup all schemas in the form of a setup/tables_current.inc.php file 394 * 395 * @param f resource/boolean 396 */ 397 function schema_backup($f=False) 398 { 399 foreach($this->adodb->MetaTables('TABLES') as $table) 400 { 401 if ($this->system_tables && preg_match($this->system_tables,$table) || 402 $this->egw_tables && !preg_match($this->egw_tables,$table)) 403 { 404 continue; 405 } 406 if ($this->db->Type == 'sapdb' || $this->db->Type == 'maxdb') 407 { 408 $table = strtolower($table); 409 } 410 if (!($this->schemas[$table] = $this->schema_proc->GetTableDefinition($table))) 411 { 412 unset($this->schemas[$table]); 413 } 414 if (($this->db->Type == 'sapdb' || $this->db->Type == 'maxdb') && $table == 'phpgw_anglemail') 415 { 416 // sapdb does not differ between text and blob 417 $this->schemas[$table]['fd']['content']['type'] = 'blob'; 418 } 419 } 420 $def = "\t\$phpgw_baseline = "; 421 $def .= $this->write_array($this->schemas,1); 422 $def .= ";\n"; 423 424 if ($f) 425 { 426 fwrite($f,$def); 427 } 428 else 429 { 430 if (!is_object($this->browser)) 431 { 432 $this->browser = CreateObject('phpgwapi.browser'); 433 } 434 $this->browser->content_header('schema-backup-'.date('YmdHi').'.inc.php','text/plain',strlen($def)); 435 echo "<?php\n\t/* eGroupWare schema-backup from ".date('Y-m-d H:i:s')." */\n\n".$def; 436 } 437 } 438 439 /** 440 * @internal copied from etemplate/inc/class.db_tools.inc.php 441 */ 442 function write_array($arr,$depth,$parent='') 443 { 444 if (in_array($parent,array('pk','fk','ix','uc'))) 445 { 446 $depth = 0; 447 } 448 if ($depth) 449 { 450 $tabs = "\n"; 451 for ($n = 0; $n < $depth; ++$n) 452 { 453 $tabs .= "\t"; 454 } 455 ++$depth; 456 } 457 $def = "array($tabs".($tabs ? "\t" : ''); 458 459 $n = 0; 460 foreach($arr as $key => $val) 461 { 462 if (!is_int($key)) 463 { 464 $def .= "'$key' => "; 465 } 466 if (is_array($val)) 467 { 468 $def .= $this->write_array($val,$parent == 'fd' ? 0 : $depth,$key); 469 } 470 else 471 { 472 if (!$only_vals && $key === 'nullable') 473 { 474 $def .= $val ? 'True' : 'False'; 475 } 476 else 477 { 478 $def .= "'$val'"; 479 } 480 } 481 if ($n < count($arr)-1) 482 { 483 $def .= ",$tabs".($tabs ? "\t" : ''); 484 } 485 ++$n; 486 } 487 $def .= "$tabs)"; 488 489 return $def; 490 } 491 } 492 /* 493 $line = '"de","ranking","use \\"yes\\", or \\"no, prefession\\"","benützen Sie \\"yes\\" oder \\"no, Beruf\\""'; 494 495 echo "<p>line='$line'</p>\n"; 496 echo "<pre>".print_r(db_backup::csv_split($line),true)."</pre>\n"; 497 */
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 17:20:01 2007 | par Balluche grâce à PHPXref 0.7 |