commit 89ea01c4290f2c1973ed0a703f832072ccc0e6f2 Author: DSB Date: Fri Jun 10 21:28:27 2011 +0000 Struggeling with relocating diff --git a/ajax/dump_ajax.php b/ajax/dump_ajax.php new file mode 100644 index 0000000..55552c1 --- /dev/null +++ b/ajax/dump_ajax.php @@ -0,0 +1,343 @@ +setConnectionCharset($dump['dump_encoding']); +// each time a new database will be dumped -> +// look for Command before dump to be executed +if ($dump['table_offset'] == - 1) + executeCommand('b'); +$dump['data'] = ''; // will hold string data to be saved to the dump file +// needed to find out if new log-messages were added +$_SESSION['temp_log'] = $_SESSION['log']; +if (! isset($dump['table_records_total'])) + $dump['table_records_total'] = 0; +$tableIndex = $dump['table_offset'] == - 1 ? 0 : $dump['table_offset']; +if ($dump['backup_done'] == 0) { + if ($dump['databases'][$dump['db_actual']]['table_count'] == 0) { + //no tables found -> prevent creation of empty backupfile + $msg = sprintf($lang['L_DUMP_NOTABLES'], $dump['db_actual']); + if ($dump['databases'][$dump['db_actual']]['prefix'] != '') { + $msg = sprintf($lang['L_DUMP_NOTABLES'], $dump['db_actual']); + $msg .= ' (' . $lang['L_WITHPRAEFIX'] . ': \'' ; + $msg .= $dump['databases'][$dump['db_actual']]['prefix']. '\')'; + } + writeToErrorLog($dump['db_actual'], '', $msg); + $dbsToBackup = array( + $dump['db_actual']); + $dump['backupdatei'] = ''; + $dump['filesize'] = 0; + checkForNextDB(); + } else { + if ($dump['table_offset'] == - 1) { + // first call for this database -> create new backup file + createNewFile(); + $dump['table_offset'] = 0; // begin with first table + $dump['table_record_offset'] = 0; + $dump['restzeilen'] = $config['minspeed']; + } + $dump['restzeilen'] = $dump['speed']; + while ($dump['restzeilen'] > 0 && $dump['table_offset'] < + $dump['databases'][$dump['db_actual']]['table_count']) { + $tableNames = $dump['databases'][$dump['db_actual']]['tables']; + $table = getValueFromIndex($tableNames, $dump['table_offset']); + if ($dump['table_record_offset'] == 0) { + $dbo->selectDb($dump['db_actual']); + // a new table begins + // optimize it? + if ($config['optimize_tables_beforedump'] == 1) { + if (true === Sql::optimizeTable($dbo, $table)) { + $dump['tables_optimized']++; + } + } + $recordOffset = 0; + $recordsTotal = 0; + $dump['progress_table_percent'] = 0; + $dump['speed'] = $config['minspeed']; + // should we dump the table structure? + if ($dump['databases'][$dump['db_actual']]['tables'][$table]['dump_structure'] > 0) { + // get create statement of table + try { + $records = $dump['databases'][$dump['db_actual']]['tables'][$table]['dump_records']; + $createStatement = getCreateString($dump['db_actual'], $table, $records); + $dump['data'] .= $createStatement; + } catch (Exception $e) { + // error reading table definition + writeToDumpFile(); // save data we have up to now + $logMsg = sprintf($lang['L_FATAL_ERROR_DUMP'], $table, $dump['db_actual']); + $readCreateError = $logMsg . ': ' . $e->getMessage(); + writeToErrorLog($config['db_actual'], '', $readCreateError, 0); + $log->write(Log::ERROR, $readCreateError); + $dump['errors'] ++; + //next table + $dump['table_offset'] ++; + $dump['table_record_offset'] = 0; + // set records of table not to be dumped + $dump['databases'][$dump['db_actual']]['tables'][$table]['dump_records'] = 0; + } + } + } + if ($dump['databases'][$dump['db_actual']]['tables'][$table]['dump_records'] ==1) { + getContent($dump['db_actual'], $table); + } else { + //jump to next table if we don't need to dump the records of this table + $dump['table_offset'] ++; + } + if (strlen($dump['data']) > $config['memory_limit']) { + writeToDumpFile(); + } + } + // create list of databases for output + $dbsToBackup = implode(', ', array_keys($dump['databases'])); + // highligth actual db + $replace = '' . $dump['db_actual'] . ''; + $dbsToBackup = str_replace($dump['db_actual'], $replace, $dbsToBackup); + // we need to get the actual table again because it might have changed + $table = getValueFromIndex($dump['databases'][$dump['db_actual']]['tables'], $dump['table_offset']); + if ($table) { + // get nr of records from dump-array + $dump['table_records_total'] = $dump['databases'][$dump['db_actual']]['tables'][$table]['records']; + if ($dump['table_records_total'] > 0) { + $percent = (100 * $dump['table_record_offset']) / $dump['table_records_total']; + $dump['progress_table_percent'] = round($percent, 2); + } else { + $dump['progress_table_percent'] = 0; + } + if ($dump['speed'] + $dump['table_record_offset'] >= + $dump['table_records_total']) { + $recordOffset = $dump['table_record_offset'] + + 1; + $recordsTotal = $dump['table_records_total']; + if ($recordsTotal == 0) { + $recordOffset = 0; + } + } else { + $recordsTotal = $dump['table_record_offset'] + $dump['speed']; + $recordOffset = $dump['table_record_offset'] + 1; + } + } else { + // looks like we've done the job + $dump['table_offset'] ++; + $dump['table_records_total'] = 0; + $table = ''; + } + writeToDumpFile(); + if ($dump['table_offset'] <= $dump['databases'][$dump['db_actual']]['table_count']) { + $dauer = time() - $dump['page_start_time']; + //Zeitanpassung + if ($dauer < $dump['max_zeit']) { + if ($dauer < $dump['max_zeit'] / 2) { + $dump['speed'] *= 1.8; + } else { + $dump['speed'] *= $config['tuning_add']; + } + if ($dump['speed'] > $config['maxspeed']) { + $dump['speed'] = $config['maxspeed']; + } + } else { + $dump['speed'] *= $config['tuning_sub']; + if ($dump['speed'] < $config['minspeed']) { + $dump['speed'] = $config['minspeed']; + } + } + $dump['speed'] = (int) $dump['speed']; + $dump['page_refreshs'] ++; + } else { + //Backup for all databases is done + $dump['data'] = "\nSET FOREIGN_KEY_CHECKS=1;"; + $dump['data'] .= "\n" . + '-- EOB' . "\n\n"; + writeToDumpFile(); + executeCommand('a'); + chmod($config['paths']['backup'] . $dump['backupdatei'], 0777); + $logMsg = sprintf($lang['L_DUMP_OF_DB_FINISHED'], $dump['db_actual']); + $log->write(Log::PHP, $logMsg); + checkForNextDB(); + } + } +} +// everything is dumped -> check for e-mail and ftp-actions +if ($dump['backup_done'] == 1) { + if (count($_SESSION['log']['files_created']) > 0) { + if (! isset($_SESSION['log']['email'])) { + // first call after backup is finished -> create todo-list + $_SESSION['log']['email'] = array(); + $_SESSION['email']['filelist'] = array(); + $_SESSION['log']['ftp'] = array(); + foreach ($_SESSION['log']['files_created'] as $file) { + if ($config['send_mail'] == 1) { + $_SESSION['log']['email'][] = $file; + } + foreach ($config['ftp'] as $index => $val) { + // build array with files to send. The key of $_SESSION['log']['ftp'] is the index of + // the ftp-connection details of the configuration profile to be used + if ($val['transfer'] == 1) { + if (! isset($_SESSION['log']['ftp'])) { + $_SESSION['log']['ftp'][$index] = array(); + } + $_SESSION['log']['ftp'][$index][] = $file; // add file to transfer + } + } + } + // don't start sending now, because we want to inform the client first and show the logentry + // log-messages will be sent to client + if ($config['send_mail'] == 1) { + $log->write(Log::PHP, $lang['L_EMAIL_START']); + } + if (count($_SESSION['log']['ftp']) > 0) { + $log->write(Log::PHP, $lang['L_FTP_START']); + } + } else { + if (count($_SESSION['log']['email']) > 0) { + // Ok we need to send an e-mail -> get index of first file + $files = $_SESSION['log']['email']; + $key = array_keys($files); + doEmail($_SESSION['log']['email'][$key[0]]); + unset($_SESSION['log']['email'][$key[0]]); // remove from array + } else { + $dump['backup_in_progress'] = 0; // all files sent + } + if ($dump['backup_in_progress'] == 0) { + // check if ftp-transfers need to be done + if (isset($_SESSION['log']['ftp']) && count($_SESSION['log']['ftp']) > 0) { + // a file needs to be transferred + $dump['backup_in_progress'] = 1; // indicate that there is still more to do + // get index of ftp-connection + $ftpConnectionIndexes = array_keys($_SESSION['log']['ftp']); + $ftpConnection = $ftpConnectionIndexes[0]; + // now get next file to be transferred + $files = $_SESSION['log']['ftp'][$ftpConnection]; + $fileKeys = array_keys($files); + if (isset($fileKeys[0])) { + $fileKey = $fileKeys[0]; + sendViaFTP($ftpConnection, $_SESSION['log']['ftp'][$ftpConnection][$fileKey]); + // remove file from todo-list + unset($_SESSION['log']['ftp'][$ftpConnection][$fileKey]); + } else { + // all files transferred for this ftp-connection -> remove connection index + unset($_SESSION['log']['ftp'][$ftpConnection]); + } + } else { + $dump['backup_in_progress'] = 0; + } + } + } + } +} +// get values to return +$r = array(); +$json = new Services_JSON(); +$r['backup_in_progress'] = $dump['backup_in_progress']; +// send vars that do not change while dumping only once +if ($dump['page_refreshs'] == 1) { + $r['tables_total'] = $dump['tables_total']; + $r['records_total'] = String::formatNumber($dump['records_total']); + $r['speed_min'] = String::formatNumber($config['minspeed']); + $r['speed_max'] = String::formatNumber($config['maxspeed']); + $r['config_file'] = $config['config_file']; + $r['dump_encoding'] = $dump['dump_encoding']; + $r['comment'] = $dump['comment'] > '' ? $dump['comment'] : '-'; +} +$r['table_records_total'] = String::formatNumber($dump['table_records_total']); +if (isset($dbsToBackup)) { + $r['dbs_to_backup'] = $dbsToBackup; +} +$r['actual_database'] = $dump['db_actual']; +$r['actual_table'] = $table; +$_SESSION['actual_table'] = $table; +$r['actual_table_nr'] = String::formatNumber($dump['table_offset_total'] + 1); +$r['page_refreshs'] = String::formatNumber($dump['page_refreshs']); +$r['filename'] = $dump['backupdatei']; +$r['filesize'] = byteOutput($dump['filesize']); +$r['record_offset_start'] = String::formatNumber($recordOffset); +$r['record_offset_end'] = String::formatNumber($recordsTotal); +$r['progressbar_table_width'] = (int) $dump['progress_table_percent'] * 3; +$r['progress_table_percent'] = String::formatNumber($dump['progress_table_percent'], 2); +$elapsed = time() - $dump['dump_start_time']; +$r['elapsed_time'] = getTimeFormat($elapsed); +if ($dump['records_total'] > 0) { + $progressOverallPercent = $dump['countdata'] * 100 / $dump['records_total']; + if ($progressOverallPercent == 0) { + $progressOverallPercent = 0.001; + } + $r['progress_overall_percent'] = String::formatNumber($progressOverallPercent, 2); + $r['progressbar_overall_width'] = $r['progress_overall_percent'] * 3; + $estimatedTime = ceil(($elapsed * 100 / $progressOverallPercent) - $elapsed); + $r['estimated_end'] = getTimeFormat($estimatedTime); +} +$r['speed'] = String::formatNumber($dump['speed']); +$r['speedbar_width'] = (int) $dump['speed'] * 100 / $config['maxspeed'] * 3; +$r['nr_of_errors'] = $dump['errors'] == 0 ? '-' : $dump['errors']; +$r['records_saved_total'] = String::formatNumber($dump['countdata']); +$r['tables_optimized'] =''; +if ($dump['tables_optimized'] > 0) { + $r['tables_optimized'] = sprintf($lang['L_NR_TABLES_OPTIMIZED'], String::formatNumber($dump['tables_optimized'])); +} +if ($msg > '') { + $r['log'] = $msg; +} +if ($config['multi_part'] == 1) { + $r['multipart_part'] = $dump['part'] - $dump['part_offset'] - 1; +} +$r['prefix'] = ''; +if (isset($dump['databases'][$dump['db_actual']]['prefix'])) { + $r['prefix'] = $dump['databases'][$dump['db_actual']]['prefix']; +} +// check if new log-messages werde added +$messages = getArrayDiffAssocRecursive($_SESSION['log'], $_SESSION['temp_log']); +if (isset($messages['actions']) && is_array($messages['actions'])) { + $r['actions'] = implode('
', $messages['actions']); +} +if (isset($messages['errors']) && is_array($messages['errors'])) { + $r['errors'] = implode('
', $messages['errors']); +} +$_SESSION['log'] = $_SESSION['log'] + $_SESSION['temp_log']; +$dump['last_db_actual'] = $dump['db_actual']; +//backup_done means that all tables are saved. The overall progress +//(emails, ftp) can still continue +if ($dump['backup_done'] == 1) { + // some values need to be decreased + $r['progressbar_table_width'] = 0; + $r['progress_table_percent'] = ''; + $r['actual_database'] = ''; + $r['actual_table'] = ''; + $r['record_offset_start'] = '-'; + $r['record_offset_end'] = '-'; + $r['table_records_total'] = '-'; + $r['actual_table_nr'] = String::formatNumber($dump['table_offset_total']); + $r['progress_overall_percent'] = String::formatNumber(100, 2); + $r['progressbar_overall_width'] = 300; + $r['speed'] = 0; + $r['speedbar_width'] = 0; +} +// save actual values to session +$_SESSION['dump'] = $dump; +echo $json->encode($r); +obend(true); \ No newline at end of file diff --git a/ajax/install_get_language_files.php b/ajax/install_get_language_files.php new file mode 100644 index 0000000..d67d048 --- /dev/null +++ b/ajax/install_get_language_files.php @@ -0,0 +1,150 @@ + 0) { + $file = $_SESSION['get_language'][count($_SESSION['get_language']) - 1]; + $call = '?a=get_language_file&v=' . $version . '&l=' . $languageToLoad ; + $call .= '&f=' . $file; + $fileData = getFileDataFromURL($updateUrl . $call); + if (false === $fileData || $fileData == '') { + $msg = 'Fatal error: error downloading file \'' . $file . '\'!'; + $msg .= ' Please try again.'; + $message[] = Html::getErrorMsg($msg); + } else { + // save file to disk + $file = $path . '/' . $file . '.php'; + $fp = @fopen($file, 'wb'); + if ($fp) { + fwrite($fp, $fileData); + fclose($fp); + if (!File::isWritable($file, 0644)) { + File::isWritable($file, 0777); + } + $msg = ' File \'' . $file . '\' saved succesfully.'; + $message[] = Html::getOkMsg($msg); + // remove file from todo list + $fileIndex = count($_SESSION['get_language']) - 1; + unset($_SESSION['get_language'][$fileIndex]); + } else { + $msg = 'Fatal error: couldn\'t write file \'' . $file; + $msg .= '\' to \'' . $path . '\''; + $message[] = Html::getErrorMsg($msg); + } + } + } else { + $inProgress = 0; + $msg = 'Finished installing language \'' . $languageToLoad; + $msg .= '\' successfully.'; + $message[] = Html::getOkMsg($msg); + } +} +$json = new Services_JSON(); +$r = array(); +$r['in_progress'] = $inProgress; // finished? 0=no +$r['error'] = 0; +if ($error) { + if (!empty($_SESSION['get_language'])) { + $msg = 'Incomplete installation of language pack: '; + $msg .= 'removing incomplete files.'; + $message[] = Html::getErrorMsg($msg); + // now we need to delete the language files + foreach ($filesToLoad as $file) { + if (file_exists($path . '/' . $file . '.php')) { + if (@unlink($path . '/' . $file . '.php')) { + $msg = 'Deleted file \'' . $file . '\' successfully.'; + $message[] = Html::getOkMsg($msg); + } else { + $msg = 'Error deleting file \'' . $file; + $msg .= '\'! Remove it using your FTP-Programm!'; + $message[] = Html::getErrorMsg($msg); + } + } + } + if (!in_array($languageToLoad, array('en', 'de'))) { + if (@rmdir($path)) { + $msg = 'Directory \'' . $languageToLoad; + $msg .= '\' deleted successfully.'; + $message[] = Html::getOkMsg($msg); + } else { + $msg = 'Error deleting directory \'' . $path . '\'!'; + $message[] = Html::getErrorMsg($msg); + } + } + } + $r['error'] = 1; // inidcate that an error occured to stop further actions + $r['in_progress'] = 0; +} +$r['message'] = implode('', $message); +if ($r['in_progress'] == 0) { + unset($_SESSION['get_language']); +} +echo $json->encode($r); diff --git a/ajax/restore_ajax.php b/ajax/restore_ajax.php new file mode 100644 index 0000000..c3b3817 --- /dev/null +++ b/ajax/restore_ajax.php @@ -0,0 +1,369 @@ + 0) +// clear temp log. Needed to find out if new log-messages were added +$_SESSION['temp_log'] = $_SESSION['log']; +if (!isset($config['language'])) { + // some server limit the number of vars that can be saved in a session + die('Incomplete session in restore_ajax.php'); +} +$restore['restore_in_progress'] = 1; +$timeElapsed = 0; +$commandsFound = 0; +try +{ + $dbo->setConnectionCharset($restore['dump_encoding']); + $dbo->selectDb($config['db_actual'], true); +} +catch (Exception $e) +{ + die($lang['L_DB_SELECT_ERROR'] . $config['db_actual'] + . $lang['L_DB_SELECT_ERROR2'] . '
' . $e->getMessage()); +} + +// open backup file +$file = $config['paths']['backup'] . $restore['filename']; +if ($restore['compressed'] == 1) { + $restore['filehandle'] = gzopen($file, 'r'); +} else { + $restore['filehandle'] = fopen($file, 'r'); +} +if (!$restore['filehandle']) { + // fatal error: we couldn't open the backup file + writeToErrorLog( + '', '', $lang['L_FILE_OPEN_ERROR'] . ': ' . $config['paths']['backup'] + . $restore['filename'], 0 + ); + die($lang['L_FILE_OPEN_ERROR'] . ': ' . $restore['filename']); +} + +$filesize = filesize($config['paths']['backup'] . $restore['filename']); +// move file pointer to the actual positon in the file +if ($restore['compressed']) { + gzseek($restore['filehandle'], $restore['offset']); +} else { + fseek($restore['filehandle'], $restore['offset']); +} +$log=new Log(); + +// Disable Keys of actual table (after page-refresh: again!) +// to speed up restoring +// but only if the table should be restored and if it already exists +$existingTables = $dbo->getTables($config['db_actual']); +$actualTable = $restore['actual_table']; +if (in_array($actualTable, $existingTables)) { + if ($restore['tables_to_restore']) { + if (in_array($actualTable, $restore['tables_to_restore'])) { + Sql::disableKeys($dbo, $restore['actual_table']); + } + } elseif ($actualTable > '' && $actualTable != 'unbekannt') { + Sql::disableKeys($dbo, $restore['actual_table']); + } +} +WHILE ($commandsFound < $restore['speed'] && + $timeElapsed < $restore['max_zeit'] && + !$restore['fileEOF'] && !$restore['EOB']) { + $sqlCommand = getQueryFromFile(); + $commandsFound++; + if ($sqlCommand > '') { + //$log->write(Log::PHP, $sqlCommand); + try + { + $res = $dbo->query($sqlCommand, MsdDbFactory::SIMPLE); + // get nr of affected rows + $command = strtoupper(substr($sqlCommand, 0, 7)); + if ($command == 'INSERT ' || $command == 'REPLACE') { + $rowsAffected = $dbo->getAffectedRows(); + $restore['records_inserted'] += $rowsAffected; + if (!isset( + $restore['records_inserted_table'][$restore['actual_table']]) + ) { + $restore['records_inserted_table'][$restore['actual_table']] = 0; + } + $restore['records_inserted_table'][$restore['actual_table']] += $rowsAffected; + } + } + catch (Exception $e) + { + // we've got a mysql error + $sqlError = $e->getMessage(); + if ($sqlError != '') { + if (strtolower(substr($sqlError, 0, 15)) == 'duplicate entry') { + writeToErrorLog( + $config['db_actual'], $sqlCommand, $sqlError, 1 + ); + $restore['notices']++; + } else { + if ($config['stop_with_error'] == 0) { + // according to the config we continue restoring + // but log the error + writeToErrorLog( + $config['db_actual'], + $sqlCommand, $sqlError, 0 + ); + $restore['errors']++; + } else { + // we should die if errors occur -> print error message + writeToErrorLog( + $config['db_actual'], + $sqlCommand, + 'Fatal error, restore failed: ' . $sqlError, 0 + ); + SQLError($sqlCommand, $sqlError); + $restore['restore_in_progress'] = 0; + die(); + // TODO clean end of process - last message is not + // logged on restore screen and + // not sent back to client. Flag missing here that + // should be handled via JSON + } + } + } + } + } + $timeElapsed = time() - $restore['page_start_time']; +} + +if ($restore['compressed']) { + $restore['offset'] = gztell($restore['filehandle']); + gzclose($restore['filehandle']); +} else { + $restore['offset'] = ftell($restore['filehandle']); + fclose($restore['filehandle']); +} +$restore['page_refreshs']++; + +// progress of actual file +if ($restore['compressed']) { + // compressed backup - there is no way to get the exact file offset, + // because gztell delivers uncompressed bytes + // so we assume the average packing factor is 11 and will divide the file + // offset by it + $restore['progress_file_percent'] = + $restore['offset'] / 11 * 100 / $filesize; + if ($restore['progress_file_percent'] > 100) + $restore['progress_file_percent'] = 100; +} else { + // uncompressed backup -> get percentage from file offset + $restore['progress_file_percent'] = 0; + if ($filesize > 0) { + $restore['progress_file_percent'] = + ($restore['offset'] * 100) / $filesize; + } + +} + +// Overall progress +if ($restore['records_total'] > 0) { + // it's a backup of MySQLDumper (the number of total records is known) -> + // calculate on count of records + $restore['progress_overall_percent'] = + $restore['records_inserted'] * 100 / $restore['records_total']; +} else { + // backup from another script. We don't know how many records will follow + $restore['progress_overall_percent'] = 0; +} +// tables to create +if ((int) $restore['tables_total'] > 0) { + // MSD-Backup + // tables to go + $tablesToDo = $restore['tables_total']; + // selected tables should be restored? Overwrite value + if (is_array($restore['tables_to_restore'])) + $tablesToDo = count($restore['tables_to_restore']); + $tablesToCreate = sprintf( + $lang['L_RESTORE_TABLES_COMPLETED'], $restore['table_ready'], $tablesToDo + ); + $recordsDone = sprintf( + $lang['L_ACTUALLY_INSERTED_RECORDS_OF'], + String::formatNumber($restore['records_inserted']), + String::formatNumber($restore['records_total']) + ); +} else { + // not a MSD-Backup + $tablesToCreate = sprintf( + $lang['L_RESTORE_TABLES_COMPLETED'], + $restore['table_ready'], + $lang['L_UNKNOWN'] + ); + $recordsDone = sprintf( + $lang['L_ACTUALLY_INSERTED_RECORDS'], + String::formatNumber($restore['records_inserted']), + $lang['L_UNKNOWN'] + ); +} + +// calculate speed for next page call +if ($timeElapsed < $restore['max_zeit']) { + // wenn wir mehr als die Haelfte der Zeit noch haetten nutzen koennen: + // Anzahl direkt um fast das Doppelte erhoehen + if ($timeElapsed < $restore['max_zeit'] / 2) + $restore['speed'] = $restore['speed'] * 1.8; + else $restore['speed'] = $restore['speed'] * $config['tuning_add']; + if ($restore['speed'] > $config['maxspeed']) + $restore['speed'] = $config['maxspeed']; +} else { + $restore['speed'] = $restore['speed'] * $config['tuning_sub']; + if ($restore['speed'] < $config['minspeed']) + $restore['speed'] = $config['minspeed']; +} + +if ($restore['fileEOF'] && $restore['part'] == 0) +$restore['EOB'] = true; //part is >0 if we have a Multipart backup +if ($restore['EOB']) { + // Done + $time = getTimeFormat(time() - $restore['restore_start_time']); + $log->write( + Log::PHP, sprintf( + $lang['L_RESTORE_DB_COMPLETE_IN'], + $config['db_actual'], + $time + ) + ); + $restore['restore_in_progress'] = 0; +} else { + if ($restore['fileEOF']) { + // let's get the next Multipart file + $restore['fileEOF'] = false; + $nextfile = getNextPart($restore['filename'], 0, true); + // there is more to do -> process the next Multipart file + if (!file_exists($config['paths']['backup'] . $nextfile)) { + writeToErrorLog( + $config['db_actual'], '', + sprintf($lang['L_ERROR_MULTIPART_RESTORE'], $nextfile) + ); + $restore['restore_in_progress'] = 0; + } else { + $restore['filename'] = $nextfile; + $restore['offset'] = 0; + $restore['part']++; + $log->write( + Log::PHP, + sprintf( + $lang['L_CONTINUE_MULTIPART_RESTORE'], + $restore['filename'] + ) + ); + } + } +} + + +// collect values to return to client +$r = array(); +$json = new Services_JSON(); +$r['restore_in_progress'] = $restore['restore_in_progress']; +if ($restore['page_refreshs'] == 1) { + // Only send on first page call because values won't change + $r['speed_min'] = String::formatNumber($config['minspeed']); + $r['speed_max'] = String::formatNumber($config['maxspeed']); + $r['dump_encoding'] = $restore['dump_encoding']; +} + +// if restore is finished and file is gzipped, the file pointer +// might not be accurate +if ($restore['restore_in_progress'] == 0) { + // correct percentage of file to now known exact value + $restore['progress_file_percent'] = 100; + $restore['speed'] = 0; +} + +$r['filename'] = $restore['filename']; +$r['nr_of_errors'] = String::formatNumber($restore['errors']); +$r['nr_of_notices'] = String::formatNumber($restore['notices']); + +$r['progress_file_percent'] = String::formatNumber( + $restore['progress_file_percent'], 2 +); +$r['progress_file_bar_width'] = round($restore['progress_file_percent'] * 3, 0); + +if ($restore['progress_overall_percent'] > 0) { + $r['progress_overall_percent'] = String::formatNumber( + $restore['progress_overall_percent'], 2 + ); + $r['progress_overall_bar_width'] = round( + $restore['progress_overall_percent'] * 3, 0 + ); +} else { + $r['progress_overall_percent'] = $lang['L_UNKNOWN']; + $r['progress_overall_bar_width'] = 0; +} +if ($restore['part'] > 0) { + $r['part'] = $restore['part']; +} +$r['tables_to_create'] = $tablesToCreate; +$r['records_done'] = $recordsDone; +$r['actual_table'] = sprintf( + $lang['L_ANALYZING_TABLE'], $restore['actual_table'] +); +$r['speed'] = String::formatNumber($restore['speed']); +$r['speedbar_width'] = round( + $restore['speed'] * 100 / $config['maxspeed'] * 3, 0 +); +$r['page_refreshs'] = String::formatNumber($restore['page_refreshs']); + +$elapsed = time() - $restore['restore_start_time']; +$r['elapsed_time'] = getTimeFormat($elapsed); + +// if we restore a MySQLDumper-Backup we know the nr of records and can +// calculate the estimated time from it +// if we restore a backup from another program we need to rely on the filesize +//which is not accurate +// when the file is gzipped, but we can't help it because there is no way to +// get the exact file pointer position in a gzipped file. +// So we give our best to guess the corect position (see line 117 above) +if ($restore['progress_overall_percent'] > 0) { + $percentageDone = $restore['progress_overall_percent']; +} else { + $percentageDone = $restore['progress_file_percent']; +} +$estimatedTime = 0; +if ($percentageDone > 0) { + $estimatedTime = (100 - $percentageDone) * ($elapsed / $percentageDone); + $r['estimated_end'] = getTimeFormat($estimatedTime); +} else { + $r['estimated_end'] = $lang['L_UNKNOWN']; +} + +// check if new log-messages have been added +$messages = getArrayDiffAssocRecursive($_SESSION['log'], $_SESSION['temp_log']); +if (isset($messages['actions']) && is_array($messages['actions'])) +$r['actions'] = implode('
', $messages['actions']); +if (isset($messages['errors']) && is_array($messages['errors'])) +$r['errors'] = implode('
', $messages['errors']); +$_SESSION['log'] = $_SESSION['log'] + $_SESSION['temp_log']; + +// save actual values to session +$_SESSION['restore'] = $restore; +echo $json->encode($r); +obend(true); diff --git a/ajax/show_log_entry.php b/ajax/show_log_entry.php new file mode 100644 index 0000000..c6fdf4f --- /dev/null +++ b/ajax/show_log_entry.php @@ -0,0 +1,121 @@ +set_filenames(array('tplLog' => 'tpl/log/log_ajax.tpl')); + +// get log filename to show +$lfile = Log::getLogfile($logType); +$tplLog->assign_vars( + array( + 'ICON_SORT' => $revers == 0 ? $icon['arrow_up'] : $icon['arrow_down'], + 'SORT_ORDER' => $revers == 0 ? 1 : 0, + 'LOG' => str_replace($config['paths']['log'], '', $lfile)) +); + +$tplLog->assign_vars( + array( + 'LOG_TYPE' => $logType, + 'REVERS' => $revers) +); + +if (file_exists($lfile)) { + $lines = ($config['logcompression'] == 1) ? gzfile($lfile) : file($lfile); + if ($revers == 1) { + $lines = array_reverse($lines); + } + $i = 1; + $entriesTotal = count($lines); + foreach ($lines as $index => $val) { + if ($index >= $offset * $entriesShown + && $index < ($offset + 1) * $entriesShown) { + // strip html in Perl-Log + $val = strip_tags($val, '
'); + $timestamp = substr($val, 0, 19); + $message = substr($val, 20); + if ($revers == 0) { + $nr = String::formatNumber($offset * $entriesShown + $i); + } else { + $nr= String::formatNumber($entriesTotal - $index); + } + $tplLog->assign_block_vars( + 'LINE', + array( + 'ROWCLASS' => $i % 2 ? 'dbrow' : 'dbrow1', + 'NR' => $nr, + 'TIMESTAMP' => $timestamp, + 'MSG' => $message) + ); + $i++; + } + } + $maxOffset = floor($entriesTotal / $entriesShown); + if ($maxOffset * $entriesShown == $entriesTotal) { + $maxOffset--; + } + + $offsetForeward = $offset < $maxOffset ? $offset + 1 : 0; + $offsetBackward = $offset > 0 ? $offset - 1 : $maxOffset; + if ($revers == 0) { + $entryTo = ($offset + 1) * $entriesShown; + if ($entryTo > $entriesTotal) { + $entryTo = $entriesTotal; + } + $pagination = sprintf( + $lang['L_SHOWING_ENTRY_X_TO_Y_OF_Z'], + String::formatNumber($offset * $entriesShown + 1), + String::formatNumber($entryTo), + String::formatNumber($entriesTotal) + ); + } else { + $total = $maxOffset * $entriesShown; + $entryFrom = $entriesTotal - ($offset * $entriesShown); + if ($entryFrom > $entriesTotal) $entryFrom = $entriesTotal; + $entryTo = $entriesTotal - (($offset + 1) * $entriesShown) + 1; + if ($entryTo < 1) { + $entryTo = 1; + } + $pagination = sprintf( + $lang['L_SHOWING_ENTRY_X_TO_Y_OF_Z'], + String::formatNumber($entryFrom), + String::formatNumber($entryTo), + String::formatNumber($entriesTotal) + ); + } + + $tplLog->assign_vars( + array( + 'OFFSET_FOREWARD' => $offsetForeward, + 'OFFSET_BACKWARD' => $offsetBackward, + 'PAGINATION_ENTRIES' => $pagination) + ); +} +$tplLog->pparse('tplLog'); +obend(true); diff --git a/ajax/show_table_field.php b/ajax/show_table_field.php new file mode 100644 index 0000000..0d284d9 --- /dev/null +++ b/ajax/show_table_field.php @@ -0,0 +1,58 @@ +set_filenames( + array( + 'tpl_sqlbrowser_table_show_tabledata' => + './tpl/sqlbrowser/table/edit_field.tpl' + ) +); + +/* + * Fetch and check _GET variables + */ +$validModes = array('new'); +$db = isset($_GET['db']) ? base64_decode($_GET['db']) : $config['db_actual']; +$tablename = isset($_GET['tablename']) ? base64_decode($_GET['tablename']) : ''; +$fieldname = isset($_GET['fieldname']) ? base64_decode($_GET['fieldname']) : ''; +$rowKey = isset($_GET['key']) ? base64_decode($_GET['key']) : ''; +$mode = 'new'; +if (isset($_GET['do']) && in_array($_GET['do'], $validModes)) { + $mode = $_GET['do']; +} +$tableInfos = getExtendedFieldInfo($db, $tablename); + +$tplSqlbrowserTableShowTabledataEntry->assign_vars(array('MODE' => $mode)); + +$row = array(); +if ($mode == 'EDIT') { + //TODO implement :) +} + +$tplSqlbrowserTableShowTabledataEntry->assign_block_vars( + 'FOOTER_' . $mode, array() +); +$tplSqlbrowserTableShowTabledataEntry->pparse( + 'tpl_sqlbrowser_table_show_tabledata' +); +obend(true); diff --git a/ajax/show_tabledata_entry.php b/ajax/show_tabledata_entry.php new file mode 100644 index 0000000..b451070 --- /dev/null +++ b/ajax/show_tabledata_entry.php @@ -0,0 +1,105 @@ +set_filenames( + array( + 'tplSqlbrowserTableShowTabledataEntry' => + './tpl/sqlbrowser/table/show_tabledata_entry.tpl' + ) +); + +/* + * Fetch and check _GET variables + */ +$validModes = array('VIEW', 'EDIT', 'NEW'); +$db = isset($_GET['db']) ? base64_decode($_GET['db']) : $config['db_actual']; +$tablename = isset($_GET['tablename']) ? base64_decode($_GET['tablename']) : ''; +$rowKey = isset($_GET['key']) ? base64_decode($_GET['key']) : ''; +$mode = 'VIEW'; +if (isset($_GET['do'])) { + $mode =strtoupper((string) $_GET['do']); +} +if (!in_array($mode, $validModes)) { + $mode = 'VIEW'; +} +$tableInfos = getExtendedFieldInfo($db, $tablename); + +$tplSqlbrowserTableShowTabledataEntry->assign_vars( + array( + 'DB_NAME' => $db, + 'TABLE_NAME' => $tablename, + 'DB_NAME_URLENCODED' => base64_encode($db), + 'TABLE_NAME_URLENCODED' => base64_encode($tablename) + ) +); + +$row = array(); + +if ($mode == 'VIEW' || $mode == 'EDIT') { + $dbo->selectDb($db); + $query = "SELECT * FROM `$tablename` WHERE $rowKey"; + $row = $dbo->query($query, MsdDbFactory::ARRAY_ASSOC); + if (false === $row || !isset($row[0])) { + //TODO clean error handling + echo "

Keine Datensätze gefunden!

"; + obend(true); + exit(); + } elseif (count($row) > 1) { + // TODO clean error handling + echo "

Mehrere Datensätze gefunden!

"; + } + $row = $row[0]; +} elseif ($mode == 'NEW') { + foreach ($tableInfos as $key => $val) { + if (isset($val['field'])) { + $row[$val['field']] = ''; + } + } +} + +foreach ($row as $key => $value) { + $keyComment = ''; + if (isset($tableInfos[$key]['comment'])) { + $keyComment = $tableInfos[$key]['comment']; + } + $templateVars = array( + 'NAME' => htmlspecialchars($key), + 'KEY' => base64_encode($key), + 'KEY_COMMENT' => Html::getJsQuote($keyComment), + 'VALUE' => htmlspecialchars($value) + ); + $tplSqlbrowserTableShowTabledataEntry->assign_block_vars( + 'FIELD_' . ($mode == 'VIEW' ? 'VIEW' : 'EDIT'), $templateVars + ); +} + +$templateVars = array('RECORD_KEY' => base64_encode($rowKey)); +$tplSqlbrowserTableShowTabledataEntry->assign_block_vars( + 'FOOTER_' . $mode, $templateVars +); +$tplSqlbrowserTableShowTabledataEntry->pparse( + 'tplSqlbrowserTableShowTabledataEntry' +); +obend(true); \ No newline at end of file diff --git a/css/msd/icons/ajax-loader.gif b/css/msd/icons/ajax-loader.gif new file mode 100644 index 0000000..5b33f7e Binary files /dev/null and b/css/msd/icons/ajax-loader.gif differ diff --git a/css/msd/icons/arrow_down.gif b/css/msd/icons/arrow_down.gif new file mode 100644 index 0000000..a0c49e7 Binary files /dev/null and b/css/msd/icons/arrow_down.gif differ diff --git a/css/msd/icons/arrow_up.gif b/css/msd/icons/arrow_up.gif new file mode 100644 index 0000000..d6dd5ad Binary files /dev/null and b/css/msd/icons/arrow_up.gif differ diff --git a/css/msd/icons/arrowleft.gif b/css/msd/icons/arrowleft.gif new file mode 100644 index 0000000..292440f Binary files /dev/null and b/css/msd/icons/arrowleft.gif differ diff --git a/css/msd/icons/browse.gif b/css/msd/icons/browse.gif new file mode 100644 index 0000000..af7bf1c Binary files /dev/null and b/css/msd/icons/browse.gif differ diff --git a/css/msd/icons/close.gif b/css/msd/icons/close.gif new file mode 100644 index 0000000..fe48d38 Binary files /dev/null and b/css/msd/icons/close.gif differ diff --git a/css/msd/icons/db.gif b/css/msd/icons/db.gif new file mode 100644 index 0000000..fe181fb Binary files /dev/null and b/css/msd/icons/db.gif differ diff --git a/css/msd/icons/delete.gif b/css/msd/icons/delete.gif new file mode 100644 index 0000000..eccb32f Binary files /dev/null and b/css/msd/icons/delete.gif differ diff --git a/css/msd/icons/download.png b/css/msd/icons/download.png new file mode 100644 index 0000000..dd525cc Binary files /dev/null and b/css/msd/icons/download.png differ diff --git a/css/msd/icons/edit.gif b/css/msd/icons/edit.gif new file mode 100644 index 0000000..c33293d Binary files /dev/null and b/css/msd/icons/edit.gif differ diff --git a/css/msd/icons/gz.gif b/css/msd/icons/gz.gif new file mode 100644 index 0000000..803ed55 Binary files /dev/null and b/css/msd/icons/gz.gif differ diff --git a/css/msd/icons/index.gif b/css/msd/icons/index.gif new file mode 100644 index 0000000..318cdc4 Binary files /dev/null and b/css/msd/icons/index.gif differ diff --git a/css/msd/icons/key_fulltext.gif b/css/msd/icons/key_fulltext.gif new file mode 100644 index 0000000..80098b3 Binary files /dev/null and b/css/msd/icons/key_fulltext.gif differ diff --git a/css/msd/icons/key_nokey.gif b/css/msd/icons/key_nokey.gif new file mode 100644 index 0000000..fe181fb Binary files /dev/null and b/css/msd/icons/key_nokey.gif differ diff --git a/css/msd/icons/key_primary.gif b/css/msd/icons/key_primary.gif new file mode 100644 index 0000000..2c20699 Binary files /dev/null and b/css/msd/icons/key_primary.gif differ diff --git a/css/msd/icons/key_unique.gif b/css/msd/icons/key_unique.gif new file mode 100644 index 0000000..80ad956 Binary files /dev/null and b/css/msd/icons/key_unique.gif differ diff --git a/css/msd/icons/minus.gif b/css/msd/icons/minus.gif new file mode 100644 index 0000000..49f6a2a Binary files /dev/null and b/css/msd/icons/minus.gif differ diff --git a/css/msd/icons/mysql_help.gif b/css/msd/icons/mysql_help.gif new file mode 100644 index 0000000..b39cde9 Binary files /dev/null and b/css/msd/icons/mysql_help.gif differ diff --git a/css/msd/icons/notok.gif b/css/msd/icons/notok.gif new file mode 100644 index 0000000..0b2d56e Binary files /dev/null and b/css/msd/icons/notok.gif differ diff --git a/css/msd/icons/ok.gif b/css/msd/icons/ok.gif new file mode 100644 index 0000000..06bb815 Binary files /dev/null and b/css/msd/icons/ok.gif differ diff --git a/css/msd/icons/openfile.gif b/css/msd/icons/openfile.gif new file mode 100644 index 0000000..8225ba3 Binary files /dev/null and b/css/msd/icons/openfile.gif differ diff --git a/css/msd/icons/plus.gif b/css/msd/icons/plus.gif new file mode 100644 index 0000000..96425a5 Binary files /dev/null and b/css/msd/icons/plus.gif differ diff --git a/css/msd/icons/progressbar_dump.gif b/css/msd/icons/progressbar_dump.gif new file mode 100644 index 0000000..35292ff Binary files /dev/null and b/css/msd/icons/progressbar_dump.gif differ diff --git a/css/msd/icons/progressbar_restore.gif b/css/msd/icons/progressbar_restore.gif new file mode 100644 index 0000000..b9def18 Binary files /dev/null and b/css/msd/icons/progressbar_restore.gif differ diff --git a/css/msd/icons/progressbar_speed.gif b/css/msd/icons/progressbar_speed.gif new file mode 100644 index 0000000..a7fca58 Binary files /dev/null and b/css/msd/icons/progressbar_speed.gif differ diff --git a/css/msd/icons/save.png b/css/msd/icons/save.png new file mode 100644 index 0000000..9bc130e Binary files /dev/null and b/css/msd/icons/save.png differ diff --git a/css/msd/icons/search.gif b/css/msd/icons/search.gif new file mode 100644 index 0000000..af7bf1c Binary files /dev/null and b/css/msd/icons/search.gif differ diff --git a/css/msd/icons/table_truncate.gif b/css/msd/icons/table_truncate.gif new file mode 100644 index 0000000..0aabf51 Binary files /dev/null and b/css/msd/icons/table_truncate.gif differ diff --git a/css/msd/icons/table_truncate_reset.gif b/css/msd/icons/table_truncate_reset.gif new file mode 100644 index 0000000..da1f273 Binary files /dev/null and b/css/msd/icons/table_truncate_reset.gif differ diff --git a/css/msd/icons/truncate.gif b/css/msd/icons/truncate.gif new file mode 100644 index 0000000..eccb32f Binary files /dev/null and b/css/msd/icons/truncate.gif differ diff --git a/css/msd/pics/bg-body.gif b/css/msd/pics/bg-body.gif new file mode 100644 index 0000000..4e94b13 Binary files /dev/null and b/css/msd/pics/bg-body.gif differ diff --git a/css/msd/pics/bg-buttons.gif b/css/msd/pics/bg-buttons.gif new file mode 100644 index 0000000..1fb2865 Binary files /dev/null and b/css/msd/pics/bg-buttons.gif differ diff --git a/css/msd/pics/bg-headings.gif b/css/msd/pics/bg-headings.gif new file mode 100644 index 0000000..0506a7b Binary files /dev/null and b/css/msd/pics/bg-headings.gif differ diff --git a/css/msd/pics/h1_logo.gif b/css/msd/pics/h1_logo.gif new file mode 100644 index 0000000..6dc5b42 Binary files /dev/null and b/css/msd/pics/h1_logo.gif differ diff --git a/css/msd/pics/loveyourdata.gif b/css/msd/pics/loveyourdata.gif new file mode 100644 index 0000000..ddcc647 Binary files /dev/null and b/css/msd/pics/loveyourdata.gif differ diff --git a/css/msd/pics/navi_bg.jpg b/css/msd/pics/navi_bg.jpg new file mode 100644 index 0000000..018cbdd Binary files /dev/null and b/css/msd/pics/navi_bg.jpg differ diff --git a/css/msd/pics/navi_bg_expert.jpg b/css/msd/pics/navi_bg_expert.jpg new file mode 100644 index 0000000..3791d98 Binary files /dev/null and b/css/msd/pics/navi_bg_expert.jpg differ diff --git a/css/msd/pics/truck_bg.png b/css/msd/pics/truck_bg.png new file mode 100644 index 0000000..3b81b9f Binary files /dev/null and b/css/msd/pics/truck_bg.png differ diff --git a/css/msd/style.css b/css/msd/style.css new file mode 100644 index 0000000..874e841 --- /dev/null +++ b/css/msd/style.css @@ -0,0 +1,906 @@ +/************************************************************************************************************************ + @MySQLDumper STYLESHEET + @name msd + @author Ingo Wagener , Daniel Schlichtholz, Christian Gresshoener + @copyright MySQLDumper - Daniel Schlichtholz + @media screen and projection +************************************************************************************************************************/ + /**************************/ /*Basics, Colors & Typography*/ + /**************************/ +* { + margin: 0px; + padding: 0px; + border: 0px; +} + +html { + text-align: left; + overflow: auto; +} + +body { + min-width: 782px; + min-height: 100%; + font-size: 62.5%; /*Resets 1em to 10px*/ + font-family: Verdana, Helvetica, Sans-Serif; + font-weight: normal; + color: #000; + text-align: left; +} + +h1 { + margin-bottom: 20px; + font-size: 2.4em; + font-weight: normal; + color: #256777; +} + +h2 { + margin-bottom: 0.92em; + padding: 4px 0px 3px 0px; + font-size: 2.2em; + font-weight: normal; + color: #256777; + border-bottom: 1px solid #c7c7c7; +} + +h3 { + margin-bottom: 12px; + padding: 3px 0px 0px 4px; + font-size: 1.8em; + font-weight: normal; + color: #256777; + background: url(pics/bg-headings.gif) repeat-x; +} + +h4 { + margin-bottom: 0.8em; + padding: 0px 4px; + font-size: 1.5em; + color: #256777; +} + +p { + margin-bottom: 1.5em; + padding-left: 4px; + font-size: 1.2em; + line-height: 1.5em; +} + +a { + color: #256777; + text-decoration: none; +} + +a:hover { + color: #e87b00; + text-decoration: none; +} + +img { + display: inline; +} + +/*IE4, IE5, IE5.5, IE6 PNG-Fix +* html img { + behavior: url(pngfix/iepngfix.htc); + } +*/ /*IE7 PNG-Fix +* + html img { + behavior: url(pngfix/iepngfix.htc); + } +*/ /**********/ /*Container*/ /**********/ +#container { + float: left; + min-width: 100%; + width: auto; + padding-bottom: 10px; + border-top: 5px solid #256777; + background: #fff url(pics/bg-body.gif) repeat-x 0 0; +} + +#header { + width: auto; + margin: 0px auto; + padding: 0px 18px; + text-align: center; +} + +#sidebar { + float: left; + min-width: 190px; + width: 19em; +} + +#content { + clear: right; + width: auto; + margin-left: 19em; + padding: 0px 18px; +} + +* html #content { + float: left; + margin-left: 0px; +} + +*+html #content { + float: left; + margin-left: 0px; +} + +*+html #content h2 { + min-width: 100%; +} + +#fullcontent { + width: auto; + margin: 0px auto; + padding: 0px 18px; + text-align: center; +} + +#footer { + clear: both; + width: auto; + margin: 0px auto; + text-align: center; +} + +#footer h4 { + margin: 0px; +} + +/*******/ /*Menu*/ /*******/ +ul.menu { + margin: 0px; + padding: 0px; + font-size: 1em; + list-style: none; +} + +ul.menu li { + border-bottom: 1px solid #ddd; +} + +ul.menu a { + padding: 0px 0.85em; + font-size: 1.2em; + line-height: 2.0em; + outline: none; + display: block; +} + +ul.menu a:hover { + background: #eee; +} + +ul.menu li.active { + border-bottom: 1px solid #256777; +} + +ul.menu li.active a,ul.menu li.active a:hover { + font-weight: bold; + color: #e87b00; + background: transparent; +} + +/******/ /*Lists*/ /******/ +ul { + margin-bottom: 1.5em; + padding-left: 18px; + font-size: 1.2em; + line-height: 1.5em; + list-style-type: disc; +} + +/********/ /*Tables*/ /********/ +table { + margin-bottom: 1.5em; + color: #000; +} + +tr.dbrow { + background-color: #fcfdfd; +} + +tr.dbrow1 { + background-color: #f8fafb; +} + +/************/ /*Formbuttons*/ /************/ +a.Formbutton { + display: inline-block; + width: auto; + height: 16px; + margin: 0px 6px 20px 0px; + padding: 3px 6px; + font: 13px verdana, arial, sans-serif; + color: #e87b00; + border: 1px solid #ddd; + background: url(pics/bg-buttons.gif) repeat-x; + white-space: nowrap; + text-decoration: none; +} + +button.Formbutton { + width: auto; + height: 24px; + margin: 0px 6px 20px 0px; + padding: 0px 6px; + font: 13px verdana, arial, sans-serif; + color: #e87b00; + border: 1px solid #ddd; + background: url(pics/bg-buttons.gif) repeat-x; + vertical-align: top; + overflow: visible; + white-space: nowrap; + cursor: pointer; +} + +input.Formbutton { + width: auto; + height: 24px; + margin: 0px 6px 20px 0px; + padding: 0px 6px; + font: 13px verdana, arial, sans-serif; + color: #e87b00; + border: 1px solid #ddd; + background: url(pics/bg-buttons.gif) repeat-x; + vertical-align: top; + overflow: visible; + white-space: nowrap; + cursor: pointer; +} + +ul.Formbutton { + margin: 0px; + padding: 0px; + font-size: 1em; + list-style: none; +} + +ul.Formbutton li { + display: inline; +} + +select.Formbutton { + width: auto; + margin: 0px 6px 0px 0px; +} + +td .Formbutton { + margin: 0px 6px 0px 0px; +} + +/*Firefox , Safari , not IE8*/ +#html#body,a.Formbutton { + margin: 0px 6px 1.5em 0px; +} + +/*IE7*/ +*+html a.Formbutton { + margin: 0px 6px 20px 0px; +} + +/*Firefox , Safari , not IE8*/ +#html#body,button.Formbutton { + margin: 0px 6px 1.5em 0px; + padding: 0px 3px 2px 3px; +} + +/*IE4 , IE5 , IE5.5 , IE6*/ +* html button.Formbutton { + padding: 0px 5px; +} + +/*IE7*/ +*+html button.Formbutton { + margin: 0px 6px 20px 0px; + padding: 0px 5px; +} + +/*Firefox , Safari , not IE8*/ +#html#body,input.Formbutton { + margin: 0px 6px 1.5em 0px; + padding: 0px 3px 2px 3px; +} + +/*IE4 , IE5 , IE5.5 , IE6*/ +* html input.Formbutton { + padding: 0px 5px; +} + +/*IE7*/ +*+html input.Formbutton { + margin: 0px 6px 20px 0px; + padding: 0px 5px; +} + +.Formbutton img { + max-height: 1.2em; + margin: 0px 6px 0px 1px; + padding: 0px 0px 3px 0px; + border: 0px; + vertical-align: middle; + display: inline; +} + +button.Formbutton img { + padding: 0px 0px 1px 0px; +} + +/*IE4 , IE5 , IE5.5 , IE6*/ +* html .Formbutton img { + padding: 0px; +} + +/*IE7*/ +*+html .Formbutton img { + padding: 0px; +} + +a.Formbutton:hover,button.Formbutton:hover,input.Formbutton:hover { + color: #256777 !important; +} + +.Formbutton:disabled { + color: #888 !important; + cursor: default; +} + +/**********/ /*Messages*/ /**********/ +.message { + display: inline-block; + padding: 6px; + font-size: 1.2em; + border: 1px solid #ff0000; + background: #ffff00 url(pics/truck_bg.png) no-repeat; + background-position: right; + z-index: 1; +} + +.message div.Growler-notice-exit { + float: right; + cursor: pointer; + margin: 0px; + background: transparent; +} + +.message div.Growler-notice-body { + padding: 6px 82px 6px 6px; +} + +.message div.Growler-notice-head { + padding: 6px; +} + +/*------------------*/ /*old CSS Build*/ /*-----------------*/ +#version { + font-size: 1.1em; + color: #4E5665; + text-align: center; +} + +#version a:hover { + color: #4E5665; +} + +#menu { + margin: 1px 0px 23px 0px; +} + +#server0 { + position: fixed; + bottom: 4px; + text-align: center; + left: 10px; + color: #000; +} + +#server1 { + position: absolute; + right: 16px; + text-align: center; + top: 10px; + z-index: 1; +} + +label { + cursor: pointer; +} + +a.ul { + text-decoration: underline; +} + +.small { + font-size: 0.9em !important; +} + +.ssmall { + font-size: 0.8em !important; +} + +td.small { + font-size: 11px !important; +} + +td.ssmall { + font-size: 10px !important; +} + +.success { + color: green; + font-weight: bold; +} + +.error { + color: #E87B00; + background-color: yellow; + font-weight: bold; +} + +.explain { + text-decoration: none; + border-bottom: 1px dotted; +} + +.explain:hover { + cursor: help; +} + +.active_db { + font-weight: bold; + border-bottom: 1px dotted; + color: #9AA2B1; +} + +table { + color: #000; +} + +table.bdr,.bdr { + border: 1px solid #ddd !important; + /* border-collapse: collapse !important; */ +} + +#fullcontent table.bdr { + margin: 0px auto; +} + +table td { + text-align: left; + vertical-align: top; + padding: 0 6px; + font-size: 12px; +} + +table th { + padding: 0 6px; +} + +fieldset { + margin: 0px; + padding: 5px; + border: 1px solid #ddd; + color: #256777; +} + +body.content legend { + font-weight: bold; + color: #256777; +} + +body.menu fieldset p { + margin: 5px 0 0; + text-align: center; +} + +/* MAIN */ +#topnavi { + list-style: none; + margin: 10px 0 20px; +} + +#topnavi li { + float: left; + margin-right: 6px; +} + +#topnavi li a { + float: left; + font: 1.1em verdana, arial, sans-serif; + border: 1px solid #ddd; + background: url(pics/bg-buttons.gif) repeat-x; + color: #E87B00; + padding: 3px 6px; + vertical-align: bottom; + cursor: pointer; + text-decoration: none; + white-space: nowrap; +} + +#topnavi li a span { + color: #256777; +} + +#topnavi li a:hover { + color: #256777; +} + +#topnavi li a:hover span { + color: #E87B00; +} + +/*Tabellen */ +table tr.dbrow { + background: #FCFDFD; +} + +table tr.dbrow1 { + background: #F8FAFB; +} + +table tr.dbrow a,table tr.dbrow1 a,table tr.dbrowsel a { + margin: 0px 3px 0px 0px; +} + +table tr.dbrowsel { + background: #F9F3ED; + color: #000; +} + +table tr.dbrowsel a:hover { + color: #E87B00; +} + +table td.treffer { + background: #000; +} + +/* Treffer bei der MySQL-Suche */ +table tr.dbrow .treffer,table tr.dbrow1 .treffer { + color: yellow; + background-color: #E87B00; +} + +table.border { + border: 1px solid #738C88; +} + +table td.sum { + background-color: red; + font-weight: bold; + text-align: right; +} + +table tr.thead th,table tr.thead td { + background: url(pics/bg-buttons.gif) repeat-x; + border: 1px solid #ddd; + color: #256777; +} + +.tdcompact { + width: 100px; + height: 16px; + overflow: hidden; + font-size: 11px; +} + +.tdnormal { + white-space: nowrap; + padding: 1px; +} + +.sqlheadmenu a { + +} + +td a.Formbutton,td button.Formbutton,td input.Formbutton { + float: none; + margin: 0px 6px 0px 0px; + display: inline-block; +} + +input.Formtext { + background: url(pics/bg-buttons.gif) repeat-x; + float: left; + width: auto; + margin: 2px 6px 0px 0px; + padding: 1px 2px; + border: 1px solid #ddd; + color: #000; + overflow: hidden; + cursor: text; +} + +#content .SQLbutton { + font-size: 11px; + background: #E4E9E8; + cursor: pointer; +} + +/* htaccess edit area */ +#content textarea { + width: 100%; + background: #FFF; + border: 1px solid #ddd; + overflow: auto; +} + +#content textarea.hta_content { + border: 0px; +} + +input.radio,input.checkbox { + background-color: transparent; +} + +/* margins and paddings for input elements */ +.radio,.checkbox,.text,.select,.margin { + margin-right: 4px; + margin-left: 10px; +} + +.noleftmargin { + margin-left: 0px !important; +} + +/* options in select lists */ +option { + padding: 0px 6px 0 6px; + border-bottom: 1px solid #eee; +} + +/* save button at left site in configuration screen */ +.save-button { + float: left; + padding-top: 12px; +} + +/* Colors for Formelements */ +input.text,input.small { + padding: 1px 2px; + border: 1px solid #256777; + background: #fff; + color: #000; +} + +input.text:disabled { + border-color: #cccccc; +} + +select { + padding: 1px; + border: 1px solid #7F9DB9; + background: #FFF; + color: #000; +} + +textarea { + background: #B3C2C0; + color: #4E5665; +} + +/* disabled textarea when editign rows in SQLBrowser */ +.off { + background-color: #ccc !important; +} + +/* for Geckos */ +input[disabled] { + color: #888 !important; +} + +/* special elements */ +.MySQLbox { + font-size: 10pt; + padding: 0px; + background: #000; + color: #fff; + border: thin solid #999999; + height: 200px; + width: 100%; + text-align: left; + overflow: auto; +} + +#content #sqlheaderbox,.sqlbox-warning { + width: 100%; + padding: 6px 0px; + background: url(pics/bg-buttons.gif) repeat-x; + border: 1px solid #ddd; + color: #256777; + white-space: nowrap; + vertical-align: top; +} + +#sqlheaderbox .Formbutton { + line-height: 14px; + margin: 0px 6px 0px 0px; +} + +#sqltextarea { + width: 100% !important; + margin-right: 30px !important; + border: 1px solid #ddd; + overflow: auto; +} + +#content #sqleditbox { + border: 1px solid #738C88; + background: #EEEEEE; + margin-bottom: 10px; +} + +#content #sqleditbox form { + margin: 10px; +} + +#content #sqleditbox p { + background: #A5B6B4; + font-weight: bold; + text-align: center; +} + +#content #sqlnewbox { + border: 1px solid #738C88; + background: #E4E9E8; +} + +#content #sqlnewbox p { + background: #A5B6B4; + font-weight: bold; + text-align: center; +} + +#content #sqloutbox { + font-size: 11px; + width: 700px; + padding: 6px; + background: #D5DDDC; + border: 1px solid #738C88; + overflow: auto; +} + +#content p.autodel { + font-size: 11px; + border-bottom: 1px dashed #fff; + margin-bottom: 12px; +} + +#content .Logbox { + font: 12px/ 1.2 "Courier New", Courier, monospace; + padding: 6px; + border: 1px solid #ddd; + height: 320px; + width: 90%; + text-align: left; + overflow: auto; +} + +#content .Logbox span { + color: #738C88; +} + +#content .backupmsg { + padding-left: 20px; + font-size: 11px; +} + +#content .backupmsg .success,#content .backupmsg a { + color: #999; + font-size: 11px; +} + +#content .backupmsg .error { + color: red; +} + +.panel { + display: block; +} + +#selectConfig { + margin: 0px 6px; + width: 180px; + height: 196px; +} + +#ilog { + border: 1px solid #ddd !important; + padding: 12px; + background-color: #fcfcfc; +} + +.center { + margin: 0px auto; + text-align: center; +} + +.left { + text-align: left; +} + +.right { + text-align: right; +} + +.middle { + vertical-align: middle; +} + +.inputsize-middle { + width: 90px; +} + +.nowrap { + white-space: nowrap; +} + +.nodisplay { + display: none; +} + +.blend-in { + border: 3px outset #DDD; + padding: 6px; + z-index: 1; + background: #FFF url(pics/bg-buttons.gif) repeat-x; +} + +/* creaet directory protection */ +#psContainer { + height: 18px; + width: 100px; + border: none; + float: left; + margin: 0; + background: url(pics/bg-buttons.gif) repeat-x; + border: 1px solid #ddd; + cursor: default; +} + +#psStrength { + background-image: url('./../../js/strength.jpg'); + height: 18px; + width: 0px; + cursor: default; +} + +/* Installation */ +#fullcontent .bdr td { + border: 1px solid #ddd; + vertical-align: middle; +} + +#download { + position: absolute; + top: 50%; + left: 50%; + width: 500px; + height: 300px; + margin-top: -150px; + margin-left: -250px; + border: 4px outset #E29126; + background-color: #EEEEEE; + padding: 12px; +} + +#download-messages { + width: 480px; + height: 220px; + overflow: auto; + top: 6px; + background-color: #ffffff; + border: 1px inset #cccccc; + padding: 10px; +} + +#close_button { + position: absolute; + width: 100px; + left: 230px; + bottom: 4px; +} \ No newline at end of file diff --git a/docs/LICENSE.txt b/docs/LICENSE.txt new file mode 100644 index 0000000..be8167b --- /dev/null +++ b/docs/LICENSE.txt @@ -0,0 +1,341 @@ +$Id$ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/docs/de/CHANGELOG.txt b/docs/de/CHANGELOG.txt new file mode 100644 index 0000000..6d29f90 --- /dev/null +++ b/docs/de/CHANGELOG.txt @@ -0,0 +1,55 @@ +$Id$ + +Changelog der wichtigsten Änderungen im Vergleich zur Version 1.22 (1.23 hat die Beta-Phase nie verlassen): + +- MySQLDumper 1.24 funktioniert nach wie vor sowohl mit PHP4 als auch mit PHP5 +- neue, helle und freundliche Optik. Der "alte" Style ist für "Nostaligiker" ebenfalls enthalten. +- bessere Speicherausnutzung +- Backup und Wiederherstellung per PHP sind im Schnitt ca. 25 Prozent schneller +- Möglichkeit beim Sichern und auch beim Wiederherstellen nur bestimmte Tabellen auszuwählen +- Nutzen von mehreren MySQL-Servern und -Usern über Konfigurationsprofile. +Damit kann man mehrere Server über eine MySQLDumper-Installation warten und sichern. +- der interne SQL-Parser ist an vielen Stellen verbessert worden (noch mehr Fremdbackups können importiert werden) +- SQLBrowser: jede Menge Bugfxies und kleinere Erweiterungen (dennoch ist der SQLBrowser noch als experimentell einzustufen) +- SQLBrowser: über die Lupe kommt man zu einer durchdachten Vollextsuche. Editiert man einen Datensatz, kommt man zur Trefferliste zurück. + Das ist recht komfortabel wenn man Stellen finden muss, von denen man nicht genau weiß in welchen Spalten sie vorkommen können. +- Tools: der Export von Daten als Datei funktionierte in 1.22 nicht. Jetzt klappt das wieder. +- die Konfiguration in der WEB-GUI wurde an einigen Stellen nochmals vereinfacht und überflüssige Parameter entfernt +(Du hast kaum noch eine Chance etwas "falsch" einzustellen. :) ) +- FTP-Übertragung kann nun optional auf bis zu 3 unterschiedliche Server gleichzeitig erfolgen +- Tabellen vom Typ VIEW und MEMORY werden nun automatisch erkannt und deren Daten korrekterweise nicht mitgesichert, wohl aber deren Struktur. +- das Verzeichnis work/structure wird nicht mehr benötigt +- die automatisch immer mit angelegten Struktur-Backups wurden entfernt +- noch besseres, internes Handling der Kodierung von Backups (Umlautproblematik) +- Fehler (auch beim Sichern) werden noch zuverlässig abgefangen und aussagekräftig im Log notiert +- Konverter: wurde neu geschrieben. Jetzt werden große Dateien beim Konvertieren automatisch in Multipart-Dateien aufgeteilt +- keine Notices in Server-Logs mehr +- Beim Anlegen von gespeicherten SQL-Befehlen können nun mehrere Queries angegeben werden, die bei Nutzung von "Command before/after backup" +nacheinander ausgeführt werden. Der Erfolg oder Mißerfolg wird im Logfile notiert. +- Beim Anlegen des Verzeichnisschutzes wird die Stärke des Kennworts visualisiert. + +crodump.pl: +- wenn die crondump.pl im Standardordner "msd_cron" aufgerufen werded kann, braucht man den "$absolute_path_of_configdir" nicht mehr editieren. +Hier findet nun eine automatische Erkennung statt. +- besseres Abfangen von Fehlern +- es wird präziser mit aussagekräftigen Nachrichten geloggt +- automatisches Löschen betrachtet Multipart-Dateien nun korrekt als 1 vollständiges Backup und funktioniert wie erwartet +- das automatische Löschen wird nicht mehr zu Beginn ausgeführt, sondern erst nach Beenden des Sicherungsvorgangs + (somit bleiben im Fehlerfall alte Backups erhalten) +- der Parameter der zu nutzenden Konfigurationsdatei kann im Aufruf nun auf 3 Arten angegeben werden. +Die fehlende Endung ".conf.php" wird bei Bedarf automatisch ergänzt: +1. config=mysqldumper.conf.php +2. config=mysqdumper.conf +3. config=mysqldumper + +- Signalhandler entfernt: +Dieser sollte eigentlich einen Abbruch des Users melden und das Skript beenden. Über einen Cronjob aufgerufen führte dies bei einigen, wenigen +Servern zu der Fehlfunktion, dass mehrere Instanzen des Skripts gestartet wurden, die lange in der Prozesliste standen und manuell beendet +werden mussten. + +... und viele weitere kleine und große Bugfixes und jede Menge Aufräumarbeiten im Code. + +Wenn Du es ganz genau wissen möchtest, dann schaue Dir das changelog auf Sourceforge an. Hier ist akribisch +jede Änderung am Code dokumentiert: + +http://mysqldumper.svn.sourceforge.net/viewvc/mysqldumper/trunk/?view=log diff --git a/docs/de/INSTALLATION.txt b/docs/de/INSTALLATION.txt new file mode 100644 index 0000000..43d3116 --- /dev/null +++ b/docs/de/INSTALLATION.txt @@ -0,0 +1,7 @@ +$Id$ + +Installation: + +- lade den Ordner mysqldumper in einen beliebigen Ordner auf Deinen Webspace hoch +- Starte das Script im Browser (http://www.deineDomain.de/DeinOrdner/mysqldumper/) +- Folge den Installationsanweisungen diff --git a/docs/de/LIESMICH.txt b/docs/de/LIESMICH.txt new file mode 100644 index 0000000..41db2ef --- /dev/null +++ b/docs/de/LIESMICH.txt @@ -0,0 +1,90 @@ +$Id$ + +MySQLDumper - Readme_de +==================== + + MySQLDumper ist ein Sicherungsprogramm für MySQL-Datenbanken, + geschrieben in PHP und Perl. Damit können Sicherungskopien der + Daten (Forum, Shop, Blog, usw.) erstellt und bei Bedarf auch + wieder hergestellt werden. Besonders bei Web-Space ohne Shell-Zugang + bietet sich MySQLDumper als sinnvolle Alternative an. + + Version 1.25 + --------------- + http://www.MySQLDumper.de/ + + Copyright (C) 2004-2010 Daniel Schlichtholz (admin@MySQLDumper.de) und mehr + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + Voraussetzungen: + PHP 4 oder höher + MySQL 4.1 oder höher + einen Web-Browser + optional Perl für das Cron-Script + + Übersicht: + Das Problem: + PHP-Skripte werden nach einer bestimmten Laufzeit abgebrochen + (normalerweise nach 30 Sekunden), und so funktioniert ein Backup mit diversen + Tools nur bis zu einer bestimmten Größe. + + Braucht das Skript länger als die vom Server zugestandene Ausführungszeit, + so wird es einfach abgebrochen! Man erhält damit keine vollständige + Sicherungskopie. Die gleiche Problematik besteht beim Wiedereinspielen eines + Backups. + + Wer einmal ein Dumpfile von Hand in viele kleinere Einzelabschnitte zerlegt hat, + um eine Datenbank wieder herzustellen, der hat leidvoll und arbeitsintensiv + erfahren, wovon wir reden. Mit MySQLDumper gehört dieser manuelle Aufwand + glücklicherweise der Vergangenheit an. + + MySQLDumper umgeht den Timeout-Error mit Hilfe eines kleinen Tricks: Er liest + nur eine bestimmte Anzahl von Datensätzen aus der Tabelle aus, merkt sich, wie + weit er gekommen ist, und ruft sich anschließend selbst auf. Dadurch erhält das + Skript bei jedem Aufruf wieder die volle Ausführungszeit und umgeht so geschickt + das Problem des Abbruchs durch den Server. Das gleiche Prinzip benutzt MySQLDumper + auch beim Wiederherstellen der Daten. + + MySQLDumper kann die Daten beim Sichern sofort packen. Auch das Wiederherstellungs- + skript kann direkt aus dieser gepackten Datei lesen, ohne dass sie auf dem Server + entpackt werden muss! Das spart Zeit, Platz und Nerven. Natürlich kann man die Datei + auch ungepackt lassen, aber spätestens beim Hochladen eines Backups weiß man dies + zu schätzen. + + Download: + Die neuste Version gibt es unter http://www.MySQLDumper.de/ + + Credits: + Finden Sie auf http://www.MySQLDumper.de/credits/ + + Installation: + Siehe INSTALL_de.txt. + + Sicherheit: + Um Ihren MySQLDumper vor unberechtigten Zugriff zu schützen, müssen Sie einen sog. + Verzeichnisschutz einrichten. Öffnen Sie dazu bitte mit dem Browser die Startseite + der MySQLDumper-Installation. Dort drücken Sie den Button 'Verzeichnisschutz erstellen' + (funktioniert nur mit dem Apache-Webserver) oder erstellen Sie den Schutz manuell. + + Änderungen: + Siehe changelog_deutsch.txt + + Support: + Hilfe finden Sie im Forum unter http://forum.MySQLDumper.de/ + + Viel Spaß! + Ihr MySQLDumper-Team \ No newline at end of file diff --git a/docs/en/CHANGELOG.txt b/docs/en/CHANGELOG.txt new file mode 100644 index 0000000..958b6b6 --- /dev/null +++ b/docs/en/CHANGELOG.txt @@ -0,0 +1,52 @@ +$Id$ + +Changelog of the most important changes compared to version 1.22 (1.23 never left the beta status): + +- MySQLDumper 1.24 is still working on PHP4 and PHP5 +- new, light and friendly style. The "old" style is still included. +- better use of RAM +- backup and restore via PHP is about 25 percent faster +- possibility to select tables when doing backup or restore +- use different configuration profiles to manage different MySQL-Server or -user. + This way you can administrate different MySQL-Server with a single MySQLDumper-Installation. +- the internal SQL-Parser has been improved (more backups from other programms can be imported) +- SQLBrowser: a lot of bugfixes and some improvements (nevertheless it must still be regarded as experimental) +- SQLBrowser: a comfortable fulltext-search lets you find text even when you don't know in which column it can occur + After editing a record you get back to the hitlist. That is very comfortable if you need to change some data. +- Tools: in version 1.22 the export of data as file didn't work. Now it is working again. +- the Web-GUI has been simplified. Some parameters have been removed. (You nearly have no chance to configure something incorrectly :) ) +- FTP-Transfer: address up to 3 ftp configurations simultaneously in one backup process +- Tables of type VIEW or MEMORY are now detected and data is not saved but the structure of the table is +- the directory work/structure is no longer needed +- the automatic "structure only" backup has been removed +- better and safer handling of encodings of backup files +- better and safer error-handling +- the backup converter has been rewritten. Now it also automatically converts big files into Multipart files. +- no notices in server-logs +- when adding SQL-Queries to the SQL-Library you can now enter more than one query. If using "command before/after backup" + these queries will be executed in a row. Success or failure is written into the log file. +- When creating a password protection the password strength is visualized. + +crodump.pl: +- when you can call crondump.pl in the standard directory "msd_cron" you no longer need to enter the + "$absolute_path_of_configdir" manually. An automatic detection was added. +- better and safer catching of errors +- logging of events is much more precise and gives you clear information what happened +- automatic deletion now regards Multipart files as one complete backup and works the way you expect it to work +- automatic deletion is done after the backup process. In case of errors this retains your old backups you might need. +- the config parameter - which configuration profile is to be used - can be set in 3 ways. The missing suffix + ".conf.php" will be added dynamically. +1. config=mysqldumper.conf.php +2. config=mysqdumper.conf +3. config=mysqldumper + +- removed signalhandler: +When crondump.pl was started via a cronjob there was a malfunction. On some, rare server this signalhandler caused a second +or third instance of the script that never stopped and stuck in the process list. In this case the process must be killed manually. + +... and many more small or big bugfixes and cleaning up of the code + +When you want to know more, just take a look at the changelog of code changes at Sourceforge. Each change +of the code is documented here: + +http://mysqldumper.svn.sourceforge.net/viewvc/mysqldumper/trunk/?view=log diff --git a/docs/en/INSTALL.txt b/docs/en/INSTALL.txt new file mode 100644 index 0000000..98464f2 --- /dev/null +++ b/docs/en/INSTALL.txt @@ -0,0 +1,7 @@ +$Id$ + +Installation: + +- upload the folder mysqldumper to any folder on your webspace +- start the script in your browser (http://www.yourDomain.de/yourFolder/mysqldumper/) +- Follow the Installation assistent diff --git a/docs/en/README.txt b/docs/en/README.txt new file mode 100644 index 0000000..9b0dd59 --- /dev/null +++ b/docs/en/README.txt @@ -0,0 +1,80 @@ +$Id$ + +MySQLDumper - Readme +==================== + + MySQLDumper is a PHP and Perl based tool for backing up MySQL databases. + You can easily dump your data into a backup file and - if needed - restore it. + It is especially suited for shared hosting webspaces, where you don't have + shell access. + + Version 1.25 + --------------- + http://www.MySQLDumper.net/ + + Copyright (C) 2004-2010 Daniel Schlichtholz (admin@MySQLDumper.de) and more + + This program is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + + Requirements: + PHP 4 or later + MySQL 4.1 or later + a web-browser + optional Perl for cronscript + + Summary: + The problem: + A PHP script has a maximum execution time that is usually set to 30 seconds on + most server installations. A script running longer than this limit will simply + stop working. This behavior makes backing up large databases impossible. Maybe + you already had this specific problem when using other tools. + + MySQLDumper uses a proprietary technique to avoid this problem. It only reads + and saves a certain amount of data, then calls itself recursively via JavaScript + and remembers how far in the backup process it was. The script then resumes + backing up from that point. + + The restore process is similar. Unlike other tools, splitting and splicing of + large backup files is no longer necessary. + + MySQLDumper can write the data directly into a compressed .gz file. The restore + script is able to read this file directly without unpacking it. You can also + use the script without compression, but using Gzip saves a lot of bandwidth. + You can even configure the script to automatically send the backup file to an + FTP account or your email adress. + + Download: + You can get the newest version at http://www.MySQLDumper.net/ + + Credits: + Please see http://www.MySQLDumper.net/credits/ + + Installation: + Please see the install_english.txt file. + + Security: + To protect MySQLDumper, you have to create a directory protection. Point your web + browser to your MySQLDumper installation and push the button 'Create + directory protection' (works only with apache) or create it manually + + Changelog: + Please see changelog_english.txt + + Support: + See support forum under http://forum.MySQLDumper.de/ + + Enjoy! + The MySQLDumper team \ No newline at end of file diff --git a/docs/fr/INSTALL.txt b/docs/fr/INSTALL.txt new file mode 100644 index 0000000..578448d --- /dev/null +++ b/docs/fr/INSTALL.txt @@ -0,0 +1,8 @@ +$Id$ + +Installation: + +- Envoyer le répertoire "mysqldumper" sur votre serveur +- Chmoder le fichier "config.php" en 777 +- D�buter le script en saisissant l'adresse suivante dans votre navigateur (http://www.votre_domaine.fr/mysqldumper/) +- Suivez les instructions d'installation du script diff --git a/docs/it/INSTALL.txt b/docs/it/INSTALL.txt new file mode 100644 index 0000000..0f19bbb --- /dev/null +++ b/docs/it/INSTALL.txt @@ -0,0 +1,8 @@ +$Id$ + +Installazione: + +- carica la cartella mysqldumper sul tuo spazio web +- metti i diritti del file config.php a 777 +- fai partire lo script nel tuo Browser (http://www.il tuo dominio.it/mysqldumper/) +- segui la installazione \ No newline at end of file diff --git a/images/logo.gif b/images/logo.gif new file mode 100644 index 0000000..fa6d5c0 Binary files /dev/null and b/images/logo.gif differ diff --git a/images/paypal-de.gif b/images/paypal-de.gif new file mode 100644 index 0000000..5f26b01 Binary files /dev/null and b/images/paypal-de.gif differ diff --git a/images/paypal-en.gif b/images/paypal-en.gif new file mode 100644 index 0000000..ad9ce69 Binary files /dev/null and b/images/paypal-en.gif differ diff --git a/inc/classes/Log.php b/inc/classes/Log.php new file mode 100644 index 0000000..86b141a --- /dev/null +++ b/inc/classes/Log.php @@ -0,0 +1,195 @@ +handle = array(); + $this->handle[self::PHP] = false; + $this->handle[self::PERL] = false; + $this->handle[self::PERL_COMPLETE] = false; + $this->handle[self::ERROR] = false; + } + /** + * Close all open file handles on destruct + * + * @return void + */ + public function __destruct() + { + if ($this->handle[self::PHP]) { + $this->_close(self::PHP); + } + if (is_resource($this->handle[self::PERL])) { + $this->_close(self::PERL); + } + if (is_resource($this->handle[self::PERL_COMPLETE])) { + $this->_close(self::PERL_COMPLETE); + } + if (is_resource($this->handle[self::ERROR])) { + $this->_close(self::ERROR); + } + } + + /** + * Open file and store file handle + * + * @param string $file + * + * @return bool + */ + private function _getFileHandle($file) + { + $filename=$this->getLogfile($file); + if ($_SESSION['config']['logcompression'] == 1) { + $fileHandle = @gzopen($filename, 'a'); + } else { + $fileHandle = @fopen($filename, 'ab'); + } + if ($fileHandle) { + $this->handle[$file] = $fileHandle; + return $this->handle[$file]; + } else { + return false; + } + } + /** + * Close a filehandle + * + * @param Loge $file The file to close + */ + private function _close($file) + { + $filename=$this->getLogfile($file); + $extension = pathinfo($filename, PATHINFO_EXTENSION); + if ($extension == 'gz') { + gzclose($this->handle[$file]); + } else { + fclose($this->handle[$file]); + } + } + /** + * Write to log file + * + * @param string $file + * @param string $message + * + * @return bool + */ + public function write($file, $message) + { + // TODO get $config-values from a config class and delete global + global $config; + if (!$this->handle[$file]) { + $this->handle[$file] = $this->_getFileHandle($file); + } + $message = strip_tags($message); + // we don't need linebreaks in the log + $search = array("\n","\r"); + $replace = array ('',''); + $message = str_replace($search, $replace, $message); + $logMessage = date('d.m.Y H:i:s') . ' '. $message . "\n"; + $_SESSION['log']['actions'][] = $logMessage; + $filename = $this->getLogfile($file); + if (@filesize($filename) > $config['log_maxsize']) { + Log::delete($file); + } + //save to log file + if ($_SESSION['config']['logcompression'] == 1) { + $res = @gzwrite($this->handle[$file], $logMessage); + } else { + $res = @fwrite($this->handle[$file], $logMessage); + } + return $res; + } + + /** + * Get log file name + * + * @param const $file + * + * @return string Filename of logfile + */ + public function getLogfile($file) + { + switch ($file) + { + case Log::PHP: + $filename = self::PHP_FILE; + break; + case Log::PERL: + $filename = self::PERL_FILE; + break; + case Log::PERL_COMPLETE: + $filename = self::PERL_COMPLETE_FILE; + break; + case Log::ERROR: + $filename = self::ERROR_FILE; + break; + default: + echo "Unknown $file: ".$file; + return; + } + if ($_SESSION['config']['logcompression'] == 1) { + $filename .= '.gz'; + } + return $filename; + } + + /** + * Delete log file and recreates it. + * + * @param string $file Filename + * + * @return void + */ + public function delete($file) + { + $filename=Log::getLogfile($file); + @unlink($filename); + if ($file == Log::PHP) { + // re-create main log + if (substr($filename, -3) == '.gz') { + $log = date('d.m.Y H:i:s') . " Log created.\n"; + if ($_SESSION['config']['logcompression'] == 1) { + $fp = @gzopen($filename, "wb"); + @gzwrite($fp, $log); + @chmod($file . '.gz', 0777); + $this->handle[$file]=$fp; + } else { + $fp = @fopen($filename, "wb"); + @fwrite($fp, $log); + @chmod($file, 0777); + $this->handle[$file]=$fp; + } + } + } + } +} \ No newline at end of file diff --git a/inc/classes/db/MsdDbFactory.php b/inc/classes/db/MsdDbFactory.php new file mode 100644 index 0000000..b36b381 --- /dev/null +++ b/inc/classes/db/MsdDbFactory.php @@ -0,0 +1,249 @@ +server = $server; + $this->user = $user; + $this->password = $password; + $this->port = $port; + $this->socket = $socket; + $this->connectionCharset = 'utf8'; + $this->connectionHandle = false; + $this->dbSelected = ''; + $this->tables = array(); + $this->metaTables = array(); + } + + /** + * Create database adapter + * + * @param string $server + * @param string $user + * @param string $password + * @param string $port + * @param string $socket + * + * @return MsdDbFactory + */ + static function getAdapter($server, $user, $password, $port = 3306, $socket = '') + { + if (function_exists('mysqli_connect') && !self::FORCE_MYSQL) { + include_once ('./inc/classes/db/mysqli/MsdDbMysqli.php'); + $db = new MsdDbMysqli($server, $user, $password, $port, $socket); + } else { + include_once ('./inc/classes/db/mysql/MsdDbMysql.php'); + $db = new MsdDbMysql($server, $user, $password, $port, $socket); + } + return $db; + } + + /** + * Establish a connection to SQL-Server. + * + * Doesn't need to be called directly because of lazy loading. + * Will be called automatically if any other method is called that + * needs a connection. + * Creates a connection to the database and stores the connection handle + * in $this->connection_handle. + * If $select_database is set, the database is selected for next queries. + * Returns true on success or false if connection couldn't be established. + * + * @param string $select_database + * @param string $connection_charset + * + * @return bool + * */ + abstract public function dbConnect($selectDatabase = false); + + /** + * Get selected database + * + * @return string + */ + abstract public function getSelectedDb(); + + /** + * Get version nr of sql server + * + * @return string + */ + abstract public function getServerInfo(); + + /** + * Get version nr of sql client + * + * @return string + */ + abstract public function getClientInfo(); + + /** + * Get all known character sets of this SQL-Server. + * + * @return array + */ + abstract public function getCharsets(); + /** + * Set character set of the MySQL-connection. + * + * Trys to set the connection charset and returns it. + * Throw Exception on failure. + * + * @param string $charset + * @throws Exception + * + * @return string + */ + abstract public function setConnectionCharset($charset = 'utf8'); + /** + * Get list of databases + * + * Gets list of all databases that the actual SQL-Server-User has access to + * and saves it in $this->databases. + * Returns true on success or false on error. + * + * @return bool + */ + abstract public function getDatabases(); + + /** + * Select the given database to use it as the target for following queries. + * + * Returns true if selection was succesfull otherwise false. + * + * @throws Exception + * @param string $database + * + * @return bool + */ + abstract public function selectDb($database); + + /** + * Execute a SQL-Server-Query + * + * @param $query The query to execute + * @param $kind Type of result set + * + * @return array + */ + abstract public function query($query, $kind = self::ARRAY_OBJECT); + + /** + * Get table list of given database(s) + * + * Stores them in $this->tables[database] + * When no database is given, all databases are scanned for tables. + * Returns table list for selected or for all databases. + * + * @param string|array $database + * + * @return array + */ + abstract public function getTables($database = false); + + /** + * Gets extended table information for one or all tables + * + * @param string $table + * @param string $database + * + * @return array + */ + abstract public function getTableStatus($table = false, $database = false); + + /** + * Returns the CREATE Statement of a table. + * + * @throws Exception + * @param string $table Get CREATE-Statement for this table + * @param string $database Database holding the table + * + * @return string Create statement + */ + abstract public function getTableCreate($table, $database = false); + + /** + * Gets the full description of all columns of a table + * + * Saves list to $this->metaTables[$database][$table]. + * + * @param string $table Table to read meta info from + * @param string $database Database holding the table + * + * @return array + */ + abstract public function getTableColumns($table, $database = false); + + /** + * Gets the number of affected rows of the last query + * + * @return int + */ + abstract function getAffectedRows(); + + /** + * Escape a value for inserting it in query + * + * @param string $val + * + * @return string + */ + abstract function escape($val); + + /** + * Optimize a table. Returns true on success or MySQL-Error. + * + * @param $table string Name of table + * + * @return string|bool Returned optimize message or false on error + */ + abstract function optimizeTable($table); + + /** + * Handles a SQL-Error + * + * @param string $errmsg + * @param int $errno + * @throws MsdEception + * + * @return void + */ + public function sqlError($errmsg, $errno) + { + throw new Exception($errmsg, $errno); + } + +} diff --git a/inc/classes/db/mysql/MsdDbMysql.php b/inc/classes/db/mysql/MsdDbMysql.php new file mode 100644 index 0000000..de823ae --- /dev/null +++ b/inc/classes/db/mysql/MsdDbMysql.php @@ -0,0 +1,439 @@ +charsets)) return $this->charsets; + if (false === $this->connectionHandle) $this->dbConnect(); + $result = $this->query('SHOW CHARACTER SET', self::ARRAY_ASSOC); + $this->charsets = array(); + foreach ($result as $r) { + $this->charsets[$r['Charset']] = $r; + } + @ksort($this->charsets); + return $this->charsets; + } + /** + * Establish a connection to MySQL. + * + * Creates a connection to the database and stores the connection handle in + * $this->connectionHandle. + * If $select_database is set, the database is selected for further queries. + * Returns true on success or false if connection couldn't be established. + * + * @param string $select_database + * @param string $connectionCharset + * + * @return bool + **/ + public function dbConnect($selectDatabase = false) + { + if (is_resource($this->connectionHandle)) return true; + + $connectionString = $this->server . ':' . $this->port; + if ($this->socket > '') $connectionString .= ':' . $this->socket; + if (false === $this->connectionHandle) { + $this->connectionHandle = @mysql_connect( + $connectionString, + $this->user, $this->password + ); + if (false === $this->connectionHandle) { + return '(' . mysql_errno() . ') ' . mysql_error(); + } + } + $this->setConnectionCharset($this->connectionCharset); + if (false === $selectDatabase && $this->dbSelected > '') { + $selectDatabase = $this->dbSelected; + } + if ($selectDatabase) { + try + { + $this->selectDb($selectDatabase, $this->connectionHandle); + $this->dbSelected = $selectDatabase; + return true; + } catch (Exception $e) { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + return false; + } + } + return true; + } + + /** + * Get version nr of sql server + * + * @return string + */ + public function getServerInfo() + { + if (false === $this->connectionHandle) { + $this->dbConnect(); + } + $version = mysql_get_server_info($this->connectionHandle); + return $version; + } + /** + * Get version nr of sql client + * + * @return string + */ + public function getClientInfo() + { + if (false === $this->connectionHandle) $this->dbConnect(); + $version = mysql_get_client_info(); + return $version; + } + + /** + * Set character set of the MySQL-connection. + * + * Trys to set the connection charset and returns it. + * Throw Exception on failure. + * + * @param string $charset + * @throws Exception + * + * @return string + */ + public function setConnectionCharset($charset = 'utf8') + { + if (false === $this->connectionHandle) $this->dbConnect(); + if (function_exists('mysql_set_charset') + && @mysql_set_charset($charset, $this->connectionHandle)) { + $this->connectionCharset = $charset; + return $this->connectionCharset; + } else { + $this->query('SET NAMES \'' . $charset . '\'', self::SIMPLE); + $this->connectionCharset = $charset; + return $charset; + } + } + + /** + * Get list of databases + * + * Gets list of all databases that the actual MySQL-User has access to + * and saves it in $this->databases. + * Returns true on success or false on error. + * + * @return array List of found database names + */ + public function getDatabases() + { + if (false === $this->connectionHandle) $this->dbConnect(); + $this->databases = array(); + $databases = @mysql_list_dbs($this->connectionHandle); + if (is_resource($databases)) { + WHILE ($row = mysql_fetch_array($databases, MYSQL_ASSOC)) { + $this->databases[] = $row['Database']; + } + } else { + //mysql_list_dbs seems not to be allowed for this user + // try to get list via "SHOW DATABASES" + $res = $this->query('SHOW DATABASES', self::ARRAY_ASSOC); + foreach ($res as $r) { + $this->databases[] = $r['Database']; + } + } + sort($this->databases); + return $this->databases; + } + /** + * Select the given database to use it as the target for following queries + * + * Returns true if selection was succesfull otherwise false. + * + * @throws Exception + * @param string $database + * + * @return bool + */ + public function selectDb($database) + { + if (!is_resource($this->connectionHandle)) { + $this->dbConnect(); + } + $res=@mysql_select_db($database, $this->connectionHandle); + if ($res===false) { + return mysql_error(); + } else { + $this->dbSelected = $database; + return true; + } + } + + /** + * Get selected database + * + * @see inc/classes/db/MsdDbFactory#getSelectedDb() + * + * @return string + */ + public function getSelectedDb() + { + return $this->dbSelected; + } + + /** + * Execute a query + * + * @param $query The query to execute + * @param $kind Type of result set + * + * @return array + */ + public function query($query, $kind = self::ARRAY_OBJECT) + { + if (false === $this->connectionHandle) { + $this->dbConnect(); + } + $res = @mysql_query($query, $this->connectionHandle); + if (false === $res) { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + } + if ($kind === self::SIMPLE || is_bool($res)) { + return $res; + } + $ret = array(); + if ($kind === self::ARRAY_OBJECT) { + WHILE ($row = mysql_fetch_object($res)) { + $ret[] = $row; + } + } elseif ($kind === self::ARRAY_NUMERIC) { + WHILE ($row = mysql_fetch_array($res, MYSQL_NUM)) { + $ret[] = $row; + } + } elseif ($kind === self::ARRAY_ASSOC) { + WHILE ($row = mysql_fetch_array($res, MYSQL_ASSOC)) { + $ret[] = $row; + } + } + return $ret; + } + /** + * Get table list of given database(s) + * + * Stores them in $this->tables[database] + * When no database is given, all databases are scanned for tables. + * Returns table list for selected or for all databases. + * + * @param string|array $database + * + * @return array + */ + public function getTables($database = false) + { + if (false === $this->connectionHandle) $this->dbConnect(); + if (!isset($this->tables)) $this->tables = array(); + if ($database !== false) { + //list tables of selected database + if (is_array($database)) { + $databases = $database; + } else { + $databases = array(); + $databases[0] = $database; + } + } else { + //list tables for all databases + $this->getDatabases(); + $databases = $this->databases; + } + // get tablenames inside each database + foreach ($databases as $db) { + $this->tables[$db] = array(); + $sql = 'SHOW TABLES FROM `' . $db . '`'; + $res = $this->query($sql, self::ARRAY_NUMERIC); + foreach ($res as $val) { + $this->tables[$db][] = $val[0]; + } + } + return is_string($database) ? $this->tables[$database] : $this->tables; + } + + /** + * Gets extended table information for one or all tables + * + * @param string $table + * @param string $database + * + * @return array + */ + public function getTableStatus($table = false, $database = false) + { + if ($database !== false && $database != $this->dbSelected) { + $this->selectDb($database); + } elseif (!$this->_mysqli instanceof mysqli) { + $this->selectDb($this->dbSelected); + } + if (!isset($this->tableinfo)) { + $this->tableinfo = array(); + } + if (!isset($this->tableinfo[$this->dbSelected])) { + $this->tableinfo[$this->dbSelected] = array(); + } + $sql = 'SHOW TABLE STATUS'; + if ($table !== false) { + $sql .= ' LIKE \'' . $table . '\''; + } + $res = $this->query($sql, self::ARRAY_ASSOC); + if (is_array($res)) { + foreach ($res as $r) { + $tablename = $r['Name']; + unset($r['Name']); + $this->tableinfo[$this->dbSelected][$tablename] = $r; + } + } elseif ($res === false) { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + } + if ($table !== false) { + if (isset($this->tableinfo[$this->dbSelected][$table])) { + return $this->tableinfo[$this->dbSelected][$table]; + } + } + return $this->tableinfo[$this->dbSelected]; + } + + /** + * Returns the CREATE-Statement for the given table or false on error. + * + * @throws Exception + * @param string $table + * @param string $database + * + * @return string + */ + public function getTableCreate($table, $database = false) + { + if (false === $database) $database = $this->dbSelected; + if ($database != $this->dbSelected) { + if (false === $this->selectDB($database)) { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + } + } + if (!is_resource($this->connectionHandle)) { + $this->dbConnect($this->dbSelected); + } + $sql = 'SHOW CREATE TABLE `' . $table . '`'; + $res = $this->query($sql, self::ARRAY_ASSOC); + if (isset($res[0]['Create Table'])) { + return $res[0]['Create Table']; + } else { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + } + } + + /** + * Gets the full description of all columns of a table + * + * Saves it to $this->metaTables[$database][$table]. + * + * @param string $table + * @param string $database + * @return array + */ + public function getTableColumns($table, $database = false) + { + if (false === $database) $database = $this->dbSelected; + if ($database != $this->dbSelected) { + if (false === $this->selectDB($database)) { + $this->sqlError( + mysql_error($this->connectionHandle), + mysql_errno($this->connectionHandle) + ); + } + } + if (!is_resource($this->connectionHandle)) { + $this->dbConnect($this->dbSelected); + } + $sql = 'SHOW FULL COLUMNS FROM `' . $table . '`'; + $res = $this->query($sql, self::ARRAY_ASSOC); + if (!isset($this->metaTables[$database])) { + $this->metaTables[$database] = array(); + } + if ($res) { + $this->metaTables[$database][$table] = array(); + foreach ($res as $r) { + $this->metaTables[$database][$table][$r['Field']] = $r; + } + } + return $this->metaTables[$database][$table]; + } + + /** + * Gets the number of affected rows for the last query + * + * @see inc/classes/db/MsdDbFactory#affectedRows() + * + * @return int + */ + public function getAffectedRows() + { + return mysql_affected_rows($this->connectionHandle); + } + + /** + * Escape a value to use it in a query + * + * @see inc/classes/db/MsdDbFactory#escape($val) + * @param mixed $val The value to escape + * @return mixed + */ + public function escape($val) + { + return mysql_real_escape_string($val, $this->connectionHandle); + } + /** + * Optimize a table. Returns true on success or MySQL-Error. + * + * @param string $table Name of table + * + * @return string|bool Returned optimize message or false on error + */ + function optimizeTable($table) + { + $sql = 'OPTIMIZE TABLE `' . $this->dbSelected . '`.`' . $table . '`'; + $res = $this->query($sql, MsdDbFactory::ARRAY_ASSOC); + if (isset($res[0]['Msg_text'])) { + return $res[0]; + } else { + return false; + } + } + +} diff --git a/inc/classes/db/mysqli/MsdDbMysqli.php b/inc/classes/db/mysqli/MsdDbMysqli.php new file mode 100644 index 0000000..64da069 --- /dev/null +++ b/inc/classes/db/mysqli/MsdDbMysqli.php @@ -0,0 +1,413 @@ +charsets)) return $this->charsets; + if (!$this->_mysqli instanceof mysqli) $this->dbConnect(); + $result = $this->query('SHOW CHARACTER SET', self::ARRAY_ASSOC); + $this->charsets = array(); + foreach ($result as $r) { + $this->charsets[$r['Charset']] = $r; + } + @ksort($this->charsets); + return $this->charsets; + } + /** + * Establish a connection to MySQL. + * + * Create a connection to MySQL and store the connection handle in + * $this->connectionHandle. + * If $select_database is set, the database is selected for further queries. + * Returns true on success or false if connection couldn't be established. + * + * @param string $select_database + * @param string $connectionCharset + * @return boolean + **/ + public function dbConnect($selectDatabase = false) + { + if ($this->_mysqli instanceof mysqli) { + return true; + } + $this->_mysqli = @new mysqli( + $this->server, + $this->user, + $this->password, + $this->dbSelected, + $this->port, + $this->socket + ); + if ($this->_mysqli->connect_errno) { + $error = $this->_mysqli->connect_error; + $errno = $this->_mysqli->connect_errno; + $this->_mysqli = null; + return '(' . $errno . ') ' . $error; + } + $this->setConnectionCharset($this->connectionCharset); + if (false === $selectDatabase && $this->dbSelected > '') { + $selectDatabase = $this->dbSelected; + } + if ($selectDatabase) { + if ($this->selectDb($selectDatabase)) { + $this->dbSelected = $selectDatabase; + return true; + } else { + $this->sqlError(mysqli_error(mysqli_error(), mysqli_errno())); + } + } + return true; + } + /** + * Get version nr of sql server + * + * @return string + */ + public function getServerInfo() + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect(); + } + return $this->_mysqli->server_info; + } + /** + * Get version nr of sql client + * + * @return string + */ + public function getClientInfo() + { + if (!$this->_mysqli instanceof mysqli) $this->dbConnect(); + return $this->_mysqli->client_info; + } + /** + * Set character set of the MySQL-connection. + * + * Trys to set the connection charset and returns it. + * Throw Exception on failure. + * + * @param string $charset + * @throws Exception + * + * @return string + */ + public function setConnectionCharset($charset = 'utf8') + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect(); + } + if (!@$this->_mysqli->set_charset($charset)) { + $this->sqlError( + $charset . ' ' . $this->_mysqli->error, + $this->_mysqli->errno + ); + } + $this->connectionCharset = $this->_mysqli->character_set_name(); + return $this->connectionCharset; + } + /** + * Get list of databases + * + * Gets list of all databases that the actual MySQL-User has access to + * and saves it in $this->databases. + * Returns true on success or false on error. + * + * @return boolean + */ + public function getDatabases() + { + if (!$this->_mysqli instanceof mysqli) $this->dbConnect(); + $res = $this->query('SHOW DATABASES', self::ARRAY_ASSOC); + $this->databases = array(); + foreach ($res as $r) { + $this->databases[] = $r['Database']; + } + sort($this->databases); + return $this->databases; + } + /** + * Select the given database to use it as the target for following queries. + * + * Returns true if selection was succesfull otherwise false. + * + * @throws Exception + * @param string $database Database to select + * + * @return boolean + */ + public function selectDb($database) + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect(); + } + $res=@$this->_mysqli->select_db($database); + if ($res===false) { + return $this->_mysqli->error; + } else { + $this->dbSelected = $database; + return true; + } + } + /** + * Get selected database + * + * @see inc/classes/db/MsdDbFactory#getSelectedDb() + * @return string + */ + public function getSelectedDb() + { + return $this->dbSelected; + } + /** + * Execute a MySQL-Query + * + * @throws Exception + * @param string $query The query to execute + * @param const $kind Type of result set + * + * @return array + */ + public function query($query, $kind = self::ARRAY_OBJECT) + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect($this->dbSelected); + } + //echo "
Mysqli: executing query: " . $query; + $result = $this->_mysqli->query($query); + if (false === $result) { + $this->sqlError($this->_mysqli->error, $this->_mysqli->errno); + } + if (!$result instanceof mysqli_result || $kind === self::SIMPLE) { + return $result; + } + $ret = array(); + if ($kind === self::ARRAY_OBJECT) { + WHILE ($row = $result->fetch_object()) { + $ret[] = $row; + } + } elseif ($kind === self::ARRAY_NUMERIC) { + WHILE ($row = $result->fetch_array(MYSQLI_NUM)) { + $ret[] = $row; + } + } elseif ($kind === self::ARRAY_ASSOC) { + WHILE ($row = $result->fetch_assoc()) { + $ret[] = $row; + } + } + if ($result instanceof mysqli) { + $result->close(); + } + return $ret; + } + /** + * Get table list of given database(s) + * + * Stores them in $this->tables[database] + * When no database is given, all databases are scanned for tables. + * Returns table list for selected or for all databases. + * + * @param string|array $database + * @return array + */ + public function getTables($database = false) + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect($this->dbSelected); + } + if (!isset($this->tables)) { + $this->tables = array(); + } + if ($database !== false) { + //only list tables of selected database + if (is_array($database)) { + $databases = $database; + } else { + $databases = array(); + $databases[0] = $database; + } + } else { + //list tables for all databases + $databases = $this->databases; + } + // get tablenames for each database + foreach ($databases as $db) { + $this->tables[$db] = array(); + $sql = 'SHOW TABLES FROM `' . $db . '`'; + $res = $this->query($sql, self::ARRAY_NUMERIC); + foreach ($res as $val) { + $this->tables[$db][] = $val[0]; + } + } + return is_string($database) ? $this->tables[$database] : $this->tables; + } + /** + * Gets extended table information for one or all tables + * + * @param string $table + * @param string $database + * @return array + */ + public function getTableStatus($table = false, $database = false) + { + if ($database !== false && $database != $this->dbSelected) { + $this->selectDb($database); + } + if (!$this->_mysqli instanceof mysqli) { + $this->selectDb($this->dbSelected); + } + if (!isset($this->tableinfo)) { + $this->tableinfo = array(); + } + if (!isset($this->tableinfo[$this->dbSelected])) { + $this->tableinfo[$this->dbSelected] = array(); + } + $sql = 'SHOW TABLE STATUS'; + if ($table !== false) { + $sql .= ' LIKE \'' . $table . '\''; + } + $res = $this->query($sql, self::ARRAY_ASSOC); + if (is_array($res)) { + foreach ($res as $r) { + $tablename = $r['Name']; + unset($r['Name']); + $this->tableinfo[$this->dbSelected][$tablename] = $r; + } + } elseif ($res === false) { + $this->sqlError($this->_mysqli->error, $this->_mysqli->errno); + } + if ($table !== false) { + if (isset($this->tableinfo[$this->dbSelected][$table])) { + return $this->tableinfo[$this->dbSelected][$table]; + } + } + return $this->tableinfo[$this->dbSelected]; + } + /** + * Returns the CREATE Statement of a table. + * + * @throws Exception + * @param string $table + * @param string $database + * + * @return string + */ + public function getTableCreate($table, $database = false) + { + if (false === $database) { + $database = $this->dbSelected; + } + if ($database != $this->dbSelected) { + if (false === $this->selectDB($database)) { + $this->sqlError($this->_mysqli->error, $this->_mysqli->errno); + } + } + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect($this->dbSelected); + } + $sql = 'SHOW CREATE TABLE `' . $table . '`'; + $res = $this->query($sql, self::ARRAY_ASSOC); + if (isset($res[0]['Create Table'])) { + return $res[0]['Create Table']; + } else { + $this->sqlError($this->_mysqli->error, $this->_mysqli->errno); + } + } + /** + * Gets the full description of all columns of a table + * + * Saves it to $this->metaTables[$database][$table]. + * + * @param string $table + * @param string $database + * + * @return array + */ + public function getTableColumns($table, $database = false) + { + if (!$this->_mysqli instanceof mysqli) { + $this->dbConnect($this->dbSelected); + } + if (false === $database) { + $database = $this->dbSelected; + } + if ($database != $this->dbSelected) { + if (false === $this->selectDB($database)) { + $this->sqlError($this->_mysqli->error, $this->_mysqli->errno); + } + } + $sql = 'SHOW FULL FIELDS FROM `' . $table . '`'; + $res = $this->query($sql, self::ARRAY_ASSOC); + if (!isset($this->metaTables[$database])) $this->metaTables[$database] = array(); + if (is_array($res)) { + $this->metaTables[$database][$table] = array(); + foreach ($res as $r) { + $this->metaTables[$database][$table][$r['Field']] = $r; + } + } + return $this->metaTables[$database][$table]; + } + /** + * Gets the number of affected rows for the last query + * + * @see inc/classes/db/MsdDbFactory#affectedRows() + * @return integer + */ + public function getAffectedRows() + { + return $this->_mysqli->affected_rows; + } + /** + * Escape a value to use it in a query + * + * @see inc/classes/db/MsdDbFactory#escape($val) + * @param mixed $val The value to escape + * + * @return mixed + */ + public function escape($val) + { + return $this->_mysqli->real_escape_string($val); + } + /** + * Optimize a table. Returns true on success or MySQL-Error. + * + * @param string $table Name of table + * + * @return boolean or string true on success or mysql_error() on failure + */ + function optimizeTable($table) + { + $sql = 'OPTIMIZE TABLE `' . $this->dbSelected . '`.`' . $table . '`'; + $res = $this->query($sql, MsdDbFactory::ARRAY_ASSOC); + if (isset($res[0]['Msg_text'])) { + return $res[0]; + } else { + return false; + } + } + +} diff --git a/inc/classes/helper/File.php b/inc/classes/helper/File.php new file mode 100644 index 0000000..7cee0b8 --- /dev/null +++ b/inc/classes/helper/File.php @@ -0,0 +1,51 @@ + $val) { + $r .= '' . "\n"; + } + return $r; + } + + /** + * Get HTML output for attribute "checked" + * + * @param string $val The current value + * @param string $checked The value for the element if it should be checked + * @return string Html attribute "checked" or empty string + */ + public static function getChecked($val, $checked) + { + return $val == $checked ? ' checked="checked"' : ''; + } + + /** + * Get HTML output for attribute "disable" + * + * @param string $val The current value + * @param string $disabled The value for the element if disabled + * + * @return string Html attribute "disabled" or empty string + */ + public static function getDisabled($val, $disabled) + { + return $val == $disabled ? ' disabled="disabled"' : ''; + } + /** + * Get HTML output for attribute "selected" + * + * @param string $val The current value + * @param string $selected The value for the element if selected + * + * @return string Html attribute "selected" or empty string + */ + public static function getSelected($val, $selected) + { + return $val == $selected ? ' selected="selected"' : ''; + } + + /** + * Convert HTML br's to new lines. + * + * @param string $str The string to convert + * @return string Converted string + */ + public static function br2nl($str) + { + $search = array('
', '
', '
'); + $replace = array("\n", "\n", "\n"); + return str_replace($search, $replace, $str); + } + + /** + * Escape quotes and/or slashes and double quotes. + * + * Used for escaping strings in JS-alerts and config-files. + * + * @param string $string String to escape + * @param boolean $escapeSlashes Escape slashes and double quotes + * + * @return string Escaped string + */ + public static function getJsQuote($string, $escapeSlashes = false) + { + if ($escapeSlashes) { + $string = str_replace('/', '\/', $string); + $string = str_replace('"', '\"', $string); + } + $string = str_replace("\n", '\n', $string); + return str_replace("'", '\\\'', $string); + } + + /** + * Replace quotes with their Html entity. + * + * Used for outputing values in HTML attributes without breaking HTML + * + * @param string $string String with quotes + * + * @return string String with replaced quotes + */ + public static function replaceQuotes($string) + { + $string = str_replace("\n", '\n', $string); + return str_replace('"', '"', $string); + } + + /** + * Escape special chars according to Perl syntax. + * + * @param string $text The string to escape + * + * @return string Escaped string + */ + public static function escapeSpecialchars($string) + { + $search = array('@', '$', '\\\\', '"'); + $replace = array('\@', '\$', '\\', '\"'); + return str_replace($search, $replace, $string); + } + + /** + * Remove added slashes recursively. + * + * @param mixed $values Array or string to remove added slashes from + * + * @return mixed Array or String with recursively removed slashes + */ + public static function stripslashesDeep($values) + { + if (is_array($values)) { + $values = array_map('Html::stripslashesDeep', $values); + } else { + $values = stripslashes($values); + } + return $values; + } + + /** + * Remove whitespaces before and after string or array. + * + * @param mixed $values Array or string to remove whitespaces from + * + * @return mixed Recursively trimmed array or string + */ + public static function trimDeep($values) + { + if (is_array($values)) { + $values = array_map('Html::trimDeep', $values); + } else { + $values = trim($values); + } + return $values; + } + /** + * Build HTML-Message-String for success messages + * + * @param string $text Message to print + * + * @return string Message surrounded by HTML-Container + */ + public static function getOkMsg($text) + { + $html= sprintf('%s', $text); + $html .= ''; + $html .= ''; + $html .= '
'; + return $html; + } + /** + * Build HTML-Message-String for error messages + * + * @param string $text Message to print + * + * @return string Message surrounded by HTML-Container + */ + public static function getErrorMsg($text) + { + $html= sprintf('%s', $text); + $html .= ''; + $html .= ''; + $html .= '
'; + return $html; + } + /** + * Create IMG-Tag + * + * @param string $pic Filename of picture + * @param string $title Title for the picture (will alsobe used for alt-tag) + * + * @return string Built img-tag + */ + public static function getIcon($pic, $title = '') + { + //TODO get value from a config class and get rid of global + global $config; + $img = '%s'; + return sprintf($img, $config['files']['iconpath'], $pic, $title, $title); + } +} diff --git a/inc/classes/helper/Sql.php b/inc/classes/helper/Sql.php new file mode 100644 index 0000000..2d13a78 --- /dev/null +++ b/inc/classes/helper/Sql.php @@ -0,0 +1,87 @@ +optimizeTable($table); + if (false !== $res) { + $success=array('status', 'info','warning','note'); + if (in_array($res['Msg_type'], $success)) { + $logMsg = $lang['L_OPTIMIZE'].' `'.$dbo->dbSelected. '`.`'; + $logMsg .= $table.'`: '.$res['Msg_text']; + $log->write(Log::PHP, $logMsg); + return true; + } else { + $logMsg = sprintf($lang['L_OPTIMIZE_TABLE_ERR'], $table); + writeToErrorLog($dbo->dbSelected, $logMsg, $res['msg_text'], 0); + return false; + } + } else { + $logMsg = sprintf($lang['L_OPTIMIZE_TABLE_ERR'], $table); + writeToErrorLog($dbo->dbSelecte, $logMsg, $res['msg_text'], 0); + return false; + } + } + /** + * Creates a INSERT INTO-string + * + * "INSERT INTO (`field1`,`field2`,..)"-Command for the given table + * by looking up the meta-information. Table must exists. + * Note: Only used when restoring a backup not from MySQLDumper containing + * extended inserts. + * + * @param MsdDbFactory Database object + * @param string $table Name of table to analyze + * + * @return string $insert The name of the table extracted from the Query + **/ + public static function getInsertSyntax(MsdDbFactory $dbo, $table) + { + $insert = ''; + $columns=$dbo->getTableColumns($table); + $fields=array_keys($columns); + $insert = 'INSERT INTO `' . $table . '` (`'; + $insert .=implode('`,`', $fields) . '`)'; + return $insert; + } + /** + * Disables keys for given table + * + * @param MsdDbFactory $dbo Database object + * @param string $table Name of table + * + * @return bool + */ + public static function disableKeys(MsdDbFactory $dbo, $table) + { + $query = '/*!40000 ALTER TABLE `' . $table . '` DISABLE KEYS */'; + return $dbo->query($query, MsdDbFactory::SIMPLE); + } +} diff --git a/inc/classes/helper/String.php b/inc/classes/helper/String.php new file mode 100644 index 0000000..0fc00a4 --- /dev/null +++ b/inc/classes/helper/String.php @@ -0,0 +1,37 @@ + '') { + $tmpConfigfilename = utf8_decode(trim($_POST['new_configurationfile'])); + if (!preg_match("/^[a-z.-_]+$/i", $tmpConfigfilename, $matches)) { + $msg = sprintf( + $lang['L_ERROR_CONFIGFILE_NAME'], + $_POST['new_configurationfile'] + ); + $msg = Html::getErrorMsg($msg); + } else { + $config['config_file'] = $_POST['new_configurationfile']; + $config['cron_configurationfile'] = $_POST['new_configurationfile'] + . ".conf.php"; + $saveConfig = true; + } + + if ($saveConfig) { + if (saveConfig() == true) { + $saveConfig = false; + $msg = sprintf( + $lang['L_SUCCESS_CONFIGFILE_CREATED'], + $_POST['new_configurationfile'] + ); + $msg = Html::getOkMsg($msg); + } else { + $msg = Html::getErrorMsg($lang['L_SAVE_ERROR']); + $saveConfig = false; + } + } + } else { + $msg = Html::getErrorMsg($lang['L_NO_NAME_GIVEN']); + } +} + +if ($saveConfig) { + // validation was fine, we can write the values to the actual config file + if (saveConfig() == true) { + getConfig($config['config_file']); + if ($config['logcompression'] != $oldlogcompression) { + deleteLog(); + } + $msg = sprintf($lang['L_SAVE_SUCCESS'], $config['config_file']); + $msg = Html::getOkMsg($msg); + } else { + $msg = Html::getErrorMsg($lang['L_SAVE_ERROR']); + } +} +include ('./inc/configuration/config_files.php'); +include ('./inc/configuration/config_menu.php'); +include ('./inc/configuration/footer.php'); diff --git a/inc/configuration/autodelete.php b/inc/configuration/autodelete.php new file mode 100644 index 0000000..b064fc4 --- /dev/null +++ b/inc/configuration/autodelete.php @@ -0,0 +1,46 @@ +set_filenames( + array( + 'tplConfigurationAutodelete' => 'tpl/configuration/autodelete.tpl') +); + +$tplConfigurationAutodelete->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'AUTODELETE_ENABLED_SELECTED' => + Html::getChecked($config['auto_delete']['activated'], 1), + 'AUTODELETE_DISABLED_SELECTED' => + Html::getChecked($config['auto_delete']['activated'], 0), + 'MAX_BACKUP_FILES' => + (int) $config['auto_delete']['max_backup_files'], + 'MAX_BACKUP_FILES_DISABLED' => + Html::getDisabled($config['auto_delete']['activated'], 0)) +); \ No newline at end of file diff --git a/inc/configuration/config_files.php b/inc/configuration/config_files.php new file mode 100644 index 0000000..fa3a331 --- /dev/null +++ b/inc/configuration/config_files.php @@ -0,0 +1,206 @@ +' + . sprintf($lang['L_CONFIG_LOADED'], $config['config_file']) + . '

'; + } else { + getConfig($oldConfig); + $databases = $oldDatabases; + $msg = '

' + . sprintf( + $lang['L_ERROR_LOADING_CONFIGFILE'], + $config['config_file'] + ) . '

'; + } +} + +if (isset($_GET['config_delete'])) { + $deleteConfig = urldecode($_GET['config_delete']); + if ($deleteConfig == $config['config_file']) { + //actaul configuration was deleted, fall back to mysqldumper-conf + $config['config_file'] = 'mysqldumper'; + $_SESSION['config_file'] = $config['config_file']; + getConfig($config['config_file']); + } + $del = @unlink('./' . $config['paths']['config'] . $deleteConfig . '.php'); + if ($del) { + // delete Perl config file + $delFile = $config['paths']['config'] . $deleteConfig . '.conf.php'; + $del = @unlink('./' . $delFile); + } + if ($del === false) { + $msg = '

' + . sprintf($lang['L_ERROR_DELETING_CONFIGFILE'], $deleteConfig) + . '

'; + } else { + $msg = '

' + . sprintf($lang['L_SUCCESS_DELETING_CONFIGFILE'], $deleteConfig) + . '

'; + } + $sel = 'configs'; +} + +$tplConfigurationConfigFiles = new MSDTemplate(); +$tplConfigurationConfigFiles->set_filenames( + array( + 'tplConfigurationConfigFiles' => 'tpl/configuration/configFiles.tpl' + ) +); +$tplConfigurationConfigFiles->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'ICON_SEARCH' => $icon['search'], + 'ICON_EDIT' => $icon['edit'], + 'ICON_DELETE' => $icon['delete'] + ) +); +$i = 0; +$configs = getConfigFilenames(); +// iterate config files and print settings to screen +foreach ($configs as $c) { + $i++; + unset($databases); + $databases = array(); + getConfig($c); + $rowclass = ($i % 2) ? 'dbrow' : 'dbrow1'; + if ($oldConfig == $c) { + $rowclass = 'dbrowsel'; // highlight active configuration + } + // Generate configuration output + $outputstringMultisettings = ''; + $dbsToBackup = array(); + // look up which databases are set to be dumped + prepareDumpProcess(); + $dbs = array_keys($dump['databases']); + $dbsToBackup = implode(', ', $dbs); + + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW', + array( + 'ROWCLASS' => $rowclass, + 'NR' => $i, + 'CONFIG_ID' => sprintf("%03d", $i), + 'CONFIG_NAME' => $c, + 'CONFIG_NAME_URLENCODED' => urlencode($c), + 'DB_HOST' => $config['dbhost'], + 'DB_USER' => $config['dbuser'], + 'NR_OF_DATABASES' => sizeof($databases), + 'DBS_TO_BACKUP' => $dbsToBackup . ' ', + 'ATTACH_BACKUP' => + $config['email']['attach_backup'] == 1 ? + $lang['L_YES'] : $lang['L_NO'] + ) + ); + + if (count($databases) > 0) { + $a = 1; + foreach ($databases as $dbName => $val) { + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.LIST_DBS', + array( + 'ROWCLASS' => $a % 2 ? 'dbrow' : 'dbrow1', + 'NR' => $a, + 'DB_NAME_URLENCODED' => base64_encode($dbName), + 'DB_NAME' => $dbName + ) + ); + $a++; + } + } + + // is Multipart used? + if ($config['multi_part'] == 1) { + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.USE_MULTIPART', + array( + 'MULTIPART_FILESIZE' => + byteOutput($config['multipart_groesse']) + ) + ); + } + + // send mail after backup? + if ($config['send_mail'] == 1) { + $recipientsCc = implodeSubarray( + $config['email']['recipient_cc'], + 'address' + ); + if ($config['email']['recipient_name'] > '') { + $recipient = $config['email']['recipient_name']; + } else { + $recipient = $config['email']['recipient_address']; + } + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.SEND_EMAIL', + array( + 'RECIPIENT' => $recipient, + 'RECIPIENT_CC' => + $recipientsCc > '' ? $recipientsCc : $lang['L_NO'] + ) + ); + $bytes = $config['email_maxsize1'] * 1024; + if ($config['email_maxsize2'] == 2) $bytes = $bytes * 1024; + if ($config['email']['attach_backup'] == 1) { + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.SEND_EMAIL.EMAIL_MAX_SIZE', + array( + 'SIZE' => byteOutput($bytes) + ) + ); + } + } + + // FTP settings + foreach ($config['ftp'] as $ftp) { + if ($ftp['transfer'] > 0) { + $ftpSettings = sprintf( + $lang['L_FTP_SEND_TO'], $ftp['server'], $ftp['dir'] + ); + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.SEND_FTP', + array( + 'FTP_SETTINGS' => Html::replaceQuotes($ftpSettings) + ) + ); + } + } + + // Show delete-button if it is not the standard config file + if ($c != 'mysqldumper') { + $confirmDelete = sprintf($lang['L_CONFIRM_CONFIGFILE_DELETE'], $c); + $tplConfigurationConfigFiles->assign_block_vars( + 'ROW.DELETE_CONFIG', + array( + 'CONFIRM_DELETE' => Html::getJsQuote($confirmDelete) + ) + ); + } +} + +unset($databases); +$databases = array(); +$_SESSION['config_file'] = $oldConfig; +$config['config_file'] = $oldConfig; +// reload actual configuration +getConfig($oldConfig); diff --git a/inc/configuration/config_menu.php b/inc/configuration/config_menu.php new file mode 100644 index 0000000..41667ad --- /dev/null +++ b/inc/configuration/config_menu.php @@ -0,0 +1,31 @@ +set_filenames( + array( + 'tplConfigurationConfigMenu' => 'tpl/configuration/config_menu.tpl' + ) +); + +$msdMode = $lang['L_MODE_EXPERT']; +if ($config['msd_mode'] == 0) { + $msdMode = $lang['L_MODE_EASY']; +} +$tplConfigurationConfigMenu->assign_vars( + array( + 'CONFIGURATION_NAME' => $config['config_file'], + 'ICON_SAVE' => $icon['small']['save'], + 'ICON_OPEN_FILE' => $icon['small']['open_file'], + 'MSD_MODE' => $msdMode + ) +); diff --git a/inc/configuration/cronscript.php b/inc/configuration/cronscript.php new file mode 100644 index 0000000..1817c10 --- /dev/null +++ b/inc/configuration/cronscript.php @@ -0,0 +1,69 @@ + 1 + && substr($config['cron_execution_path'], -1) != '/') { + $config['cron_execution_path'] .= '/'; + } + if (isset($_POST['cron_printout'])) { + $config['cron_printout'] = (int) $_POST['cron_printout']; + } + if (isset($_POST['cron_completelog'])) { + $config['cron_completelog'] = (int) $_POST['cron_completelog']; + } + if (isset($_POST['compression'])) { + $config['cron_compression'] = (int) $_POST['compression']; + } + if (isset($_POST['cron_completelog'])) { + $config['cron_completelog'] = (int) $_POST['cron_completelog']; + } +} + +$tplConfigurationCronscript = new MSDTemplate(); +$tplConfigurationCronscript->set_filenames( + array('tplConfigurationCronscript' => 'tpl/configuration/cronscript.tpl') +); + +$tplConfigurationCronscript->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'EXTENSION_PL_SELECTED' => + Html::getChecked($config['cron_extender'], 0), + 'EXTENSION_CGI_SELECTED' => + Html::getChecked($config['cron_extender'], 1), + 'EXEC_PATH' => $config['cron_execution_path'], + 'CRON_PRINTOUT_ENABLED_SELECTED' => + Html::getChecked($config['cron_printout'], 1), + 'CRON_PRINTOUT_DISABLED_SELECTED' => + Html::getChecked($config['cron_printout'], 0), + 'CRON_COMPLETELOG_ENABLED_SELECTED' => + Html::getChecked($config['cron_completelog'], 1), + 'CRON_COMPLETELOG_DISABLED_SELECTED' => + Html::getChecked($config['cron_completelog'], 0), + 'CRON_COMMENT' => + htmlspecialchars($config['cron_comment'], ENT_COMPAT, 'UTF-8') + ) +); diff --git a/inc/configuration/databases.php b/inc/configuration/databases.php new file mode 100644 index 0000000..fe344b3 --- /dev/null +++ b/inc/configuration/databases.php @@ -0,0 +1,174 @@ + 0) { + $i = 0; + foreach ($databases as $dbName => $val) { + $databases[$dbName]['prefix'] = ''; + if (isset($_POST['dbpraefix_' . $i])) { + $databases[$dbName]['prefix'] = $_POST['dbpraefix_' . $i]; + } + $databases[$dbName]['command_before_dump'] = ''; + if (!empty($_POST['command_before_' . $i])) { + $databases[$dbName]['command_before_dump'] = + getQueryFromSqlLibrary($_POST['command_before_' . $i]); + } + $databases[$dbName]['command_after_dump'] = ''; + if (!empty($_POST['command_after_' . $i])) { + $databases[$dbName]['command_after_dump'] = + getQueryFromSqlLibrary($_POST['command_after_' . $i]); + } + if (isset($_POST['db_multidump_' . $i]) + && $_POST['db_multidump_' . $i] == "db_multidump_$i") { + $databases[$dbName]['dump'] = 1; + } else { + $databases[$dbName]['dump'] = 0; + } + $i++; + } + } + if ($config['dbhost'] != $_POST['dbhost'] + || $config['dbuser'] != $_POST['dbuser'] + || $config['dbpass'] != $_POST['dbpass'] + || $config['dbport'] != $_POST['dbport'] + || $config['dbsocket'] != $_POST['dbsocket']) { + //neue Verbindungsparameter + $blendInConnectionParams = true; + //alte Parameter sichern + $old['dbhost'] = $config['dbhost']; + $old['dbuser'] = $config['dbuser']; + $old['dbpass'] = $config['dbpass']; + $old['dbport'] = $config['dbport']; + $old['dbsocket'] = $config['dbsocket']; + //neu setzen + $config['dbhost'] = $_POST['dbhost']; + $config['dbuser'] = $_POST['dbuser']; + $config['dbpass'] = $_POST['dbpass']; + $config['dbport'] = $_POST['dbport']; + $config['dbsocket'] = $_POST['dbsocket']; + $dbo = MsdDbFactory::getAdapter( + $config['dbhost'], + $config['dbuser'], + $config['dbpass'], + $config['dbport'], + $config['dbsocket'] + ); + // try to connect with new params + $res = $dbo->dbConnect(); + if ($res === true) { + // ok - get list of databases + $dbo->getDatabases(); + setDefaultConfig(); + } else { + //something went wrong - resume old values + $config['dbhost'] = $old['dbhost']; + $config['dbuser'] = $old['dbuser']; + $config['dbpass'] = $old['dbpass']; + $config['dbport'] = $old['dbport']; + $config['dbsocket'] = $old['dbsocket']; + $msg .= '

' . $lang['L_WRONG_CONNECTIONPARS'] . ': ' . $res . '

'; + $saveConfig = false; + $dbo = MsdDbFactory::getAdapter( + $config['dbhost'], + $config['dbuser'], + $config['dbpass'], + $config['dbport'], + $config['dbsocket'] + ); + } + } + // manual adding of a database + if ($_POST['add_db_manual'] > '') { + $saveConfig = false; + $blendInConnectionParams = true; + $dbToAdd = trim($_POST['add_db_manual']); + $found = false; + // Check if we already have this one in our db list + if (isset($databases[$dbToAdd])) { + $addDbMessage = sprintf($lang['L_DB_IN_LIST'], $dbToAdd); + } else { + $dbo = MsdDbFactory::getAdapter( + $config['dbhost'], + $config['dbuser'], + $config['dbpass'], + $config['dbport'], + $config['dbsocket'] + ); + try { + $dbo->selectDb($dbToAdd, true); + addDatabaseToConfig($dbToAdd); + $saveConfig = true; + } catch (Exception $e){ + $addDbMessage = $lang['L_ERROR'] . ': (' . $e->getCode() . ') '; + $addDbMessage .= $e->getMessage(); + } + } + } +} +$tplConfigurationDatabases = new MSDTemplate(); +$tplConfigurationDatabases->set_filenames( + array('tplConfigurationDatabases' => 'tpl/configuration/databases.tpl') +); +$tplConfigurationDatabases->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'DB_HOST' => $config['dbhost'], + 'DB_USER' => $config['dbuser'], + 'DB_PASS' => $config['dbpass'], + 'DB_PORT' => $config['dbport'], + 'DB_SOCKET' => $config['dbsocket'], + 'ICON_EDIT' => $icon['edit'], + 'ICON_DOWN' => $icon['arrow_down'], + 'ICON_PLUS' => $icon['plus'], + 'ICON_MINUS' => $icon['minus']) +); +if (isset($addDbMessage) && $addDbMessage > '') + $tplConfigurationDatabases->assign_block_vars( + 'MANUAL_DB_ADD', array('MESSAGE' => $addDbMessage) +); + //Wenn Datenbanken vorhanden sind +if (count($databases) > 0) { + $dbCount = count($databases); + $tplConfigurationDatabases->assign_block_vars( + 'DBS', array('DB_COUNT' => $dbCount) + ); + $i = 0; + foreach ($databases as $dbName => $val) { + if (!isset($val['dump'])) { + $val['dump'] = 0; + } + if (!isset($val['prefix'])) { + $val['prefix'] = ''; + } + $rowclass = $i % 2 ? 'dbrow' : 'dbrow1'; + if ($dbName == $config['db_actual']) { + $rowclass = 'dbrowsel'; + } + $tplConfigurationDatabases->assign_block_vars( + 'DBS.ROW', array( + 'ROWCLASS' => $rowclass, + 'ID' => $i, + 'NR' => $i + 1, + 'DB_NAME' => $dbName, + 'DB_MULTIDUMP_ENABLED' => Html::getChecked($val['dump'], 1), + 'DB_PREFIX' => $val['prefix'], + 'COMMAND_BEFORE_BACKUP_COMBO' => getCommandDumpComboBox(0, $i, $dbName), + 'COMMAND_AFTER_BACKUP_COMBO' => getCommandDumpComboBox(1, $i, $dbName)) + ); + $i++; + } +} else { + $tplConfigurationDatabases->assign_block_vars('NO_DB', array()); +} diff --git a/inc/configuration/email.php b/inc/configuration/email.php new file mode 100644 index 0000000..dd870e6 --- /dev/null +++ b/inc/configuration/email.php @@ -0,0 +1,184 @@ + $val) { + $config['email']['recipient_cc'][$i] = array(); + $config['email']['recipient_cc'][$i]['name'] = $val['name']; + $config['email']['recipient_cc'][$i]['address'] = $val['address']; + $i++; + } + } + if (isset($_POST['email_sender_name'])) { + $config['email']['sender_name'] = $_POST['email_sender_name']; + } + if (isset($_POST['email_sender_address'])) { + $config['email']['sender_address'] = $_POST['email_sender_address']; + } + if (isset($_POST['attach_backup'])) { + $config['email']['sender_address'] = $_POST['attach_backup']; + } + if (isset($_POST['email_maxsize1'])) { + $config['email_maxsize1'] = floatval($_POST['email_maxsize1']); + } + if (isset($_POST['email_maxsize2'])) { + $config['email_maxsize2'] = $_POST['email_maxsize2']; + } + $config['email_maxsize'] = $config['email_maxsize1'] + * (($config['email_maxsize2'] == 1) ? 1024 : 1024 * 1024); + if (isset($_POST['use_mailer'])) { + $config['use_mailer'] = $_POST['use_mailer']; + } + if (isset($_POST['sendmail_call'])) { + $config['sendmail_call'] = $_POST['sendmail_call']; + } + if (isset($_POST['smtp_server'])) { + $config['smtp_server'] = $_POST['smtp_server']; + } + if (isset($_POST['smtp_user'])) { + $config['smtp_user'] = $_POST['smtp_user']; + } + if (isset($_POST['smtp_pass'])) { + $config['smtp_pass'] = $_POST['smtp_pass']; + } + if (isset($_POST['smtp_useauth'])) { + $config['smtp_useauth'] = $_POST['smtp_useauth']; + } + if (isset($_POST['smtp_usessl'])) { + $config['smtp_usessl'] = $_POST['smtp_usessl']; + } + if (isset($_POST['smtp_port'])) { + $config['smtp_port'] = (int) $_POST['smtp_port']; + } + if (isset($_POST['smtp_pop3_server'])) { + $config['smtp_pop3_server'] = (string) $_POST['smtp_pop3_server']; + } + if (isset($_POST['smtp_pop3_port'])) { + $config['smtp_pop3_port'] = (int) $_POST['smtp_pop3_port']; + } +} + +// backwards compatibilty with older configurations +if (!isset($config['email_maxsize1'])) { + $config['email_maxsize1'] = 0; +} +if (!isset($config['email_maxsize2'])) { + $config['email_maxsize2'] = 1; +} +$tplConfigurationEmail = new MSDTemplate(); +$tplConfigurationEmail->set_filenames( + array('tplConfigurationEmail' => 'tpl/configuration/email.tpl') +); + +$tplConfigurationEmail->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'ICON_PLUS' => $icon['plus'], + 'ICON_DELETE' => $icon['delete'], + 'SEND_MAIL_ENABLED_SELECTED' => Html::getChecked($config['send_mail'], 1), + 'EMAIL_DISABLED' => Html::getDisabled($config['send_mail'], 0), + 'SEND_MAIL_DISABLED_SELECTED' => Html::getChecked($config['send_mail'], 0), + 'EMAIL_RECIPIENT_NAME' => + Html::replaceQuotes($config['email']['recipient_name']), + 'EMAIL_RECIPIENT_ADDRESS' => + Html::replaceQuotes($config['email']['recipient_address']), + 'EMAIL_SENDER_NAME' => Html::replaceQuotes($config['email']['sender_name']), + 'EMAIL_SENDER_ADDRESS' => + Html::replaceQuotes($config['email']['sender_address']), + 'ATTACH_BACKUP_ENABLED_SELECTED' => + Html::getChecked($config['email']['attach_backup'], 1), + 'ATTACH_BACKUP_DISABLED_SELECTED' => + Html::getChecked($config['email']['attach_backup'], 0), + 'MAXSIZE_DISABLED' => + Html::getDisabled($config['email']['attach_backup'], 0), + 'EMAIL_MAXSIZE' => $config['email_maxsize1'], + 'EMAIL_UNIT_SIZE_KB_SELECTED' => + Html::getSelected($config['email_maxsize2'], 1), + 'EMAIL_UNIT_SIZE_MB_SELECTED' => + Html::getSelected($config['email_maxsize2'], 2), + 'EMAIL_USE_PHPMAIL_SELECTED' => Html::getChecked($config['use_mailer'], 0), + 'EMAIL_USE_SENDMAIL_SELECTED' => Html::getChecked($config['use_mailer'], 1), + 'EMAIL_USE_SMTP_SELECTED' => Html::getChecked($config['use_mailer'], 2), + 'SENDMAIL_CALL' => Html::replaceQuotes($config['sendmail_call']), + 'SMTP_SERVER' => $config['smtp_server'], + 'SMTP_USER' => $config['smtp_user'], + 'SMTP_PASS' => $config['smtp_pass'], + 'SMTP_AUTH_DISABLED' => Html::getDisabled($config['smtp_useauth'], 0), + 'SMTP_AUTH_SELECTED' => Html::getChecked($config['smtp_useauth'], 1), + 'SMTP_AUTH_NOT_SELECTED' => Html::getChecked($config['smtp_useauth'], 0), + 'SMTP_SSL_SELECTED' => Html::getChecked($config['smtp_usessl'], 1), + 'SMTP_SSL_NOT_SELECTED' => Html::getChecked($config['smtp_usessl'], 0), + 'SMTP_PORT' => $config['smtp_port'], + 'SMTP_POP3_SERVER' => $config['smtp_pop3_server'], + 'SMTP_POP3_PORT' => $config['smtp_pop3_port']) +); +if ($config['smtp_useauth'] == 0) { + $tplConfigurationEmail->assign_block_vars('HIDE_SMTP_AUTH_FIELDS', array()); +} +if (is_array($config['email']['recipient_cc']) + && sizeof($config['email']['recipient_cc']) > 0) { + foreach ($config['email']['recipient_cc'] as $key => $val) { + $confirmRecDelete = sprintf( + $lang['L_CONFIRM_RECIPIENT_DELETE'], + $val['name'] . ' ' . $val['address'] + ); + $confirmRecDelete = Html::replaceQuotes($confirmRecDelete); + + $tplConfigurationEmail->assign_block_vars( + 'EMAIL_RECIPIENT_CC', + array( + 'NR' => $key, + 'CONFIRM_RECIPIENT_DELETE' => $confirmRecDelete, + 'EMAIL_RECIPIENT_CC_NAME' => Html::replaceQuotes($val['name']), + 'EMAIL_RECIPIENT_CC_ADDRESS' => + Html::replaceQuotes($val['address']) + ) + ); + } +} \ No newline at end of file diff --git a/inc/configuration/footer.php b/inc/configuration/footer.php new file mode 100644 index 0000000..0003929 --- /dev/null +++ b/inc/configuration/footer.php @@ -0,0 +1,44 @@ +set_filenames( + array('tplConfigurationFooter' => 'tpl/configuration/footer.tpl') +); + +$tplConfigurationFooter->assign_vars( + array( + 'SELECTED_DIV' => $sel, + 'NOTIFICATION_POSITION' => $config['notification_position'] + ) +); + +//output notification message if we have one +if ($msg > '') { + $tplConfigurationFooter->assign_block_vars( + 'MESSAGE', array('TEXT' => Html::getJsQuote($msg, true)) + ); +} +// if something went wrong with the change of a user or no database was found +// -> blend in connection params and let the user correct it +if ( (isset($blendInConnectionParams) && $blendInConnectionParams) + || count($databases) == 0) { + $tplConfigurationFooter->assign_block_vars( + 'SWITCH_TO_CONNECTION_PARAMETER', array() + ); +} \ No newline at end of file diff --git a/inc/configuration/ftp.php b/inc/configuration/ftp.php new file mode 100644 index 0000000..27dd91b --- /dev/null +++ b/inc/configuration/ftp.php @@ -0,0 +1,156 @@ + $val) { + // set default values if this connection is a new one + if (!isset($config['ftp'][$key]) || !is_array($config['ftp'][$key])) { + $config['ftp'][$key] = array(); + } + if (!isset($config['ftp'][$key]['server'])) { + $config['ftp'][$key]['server'] = ''; + } + if (!isset($config['ftp'][$key]['transfer'])) { + $config['ftp'][$key]['transfer'] = 0; + } + if (!isset($config['ftp'][$key]['timeout'])) { + $config['ftp'][$key]['timeout'] = 10; + } + if (!isset($config['ftp'][$key]['ssl'])) { + $config['ftp'][$key]['ssl'] = 0; + } + if (!isset($config['ftp'][$key]['mode'])) { + $config['ftp'][$key]['mode'] = 0; + } + if (!isset($config['ftp'][$key]['port'])) { + $config['ftp'][$key]['port'] = 21; + } + if (!isset($config['ftp'][$key]['user'])) { + $config['ftp'][$key]['user'] = ''; + } + if (!isset($config['ftp'][$key]['pass'])) { + $config['ftp'][$key]['pass'] = ''; + } + if (!isset($config['ftp'][$key]['dir'])) { + $config['ftp'][$key]['dir'] = '/'; + } + if (isset($val['transfer'])) { + $config['ftp'][$key]['transfer'] = (int) $val['transfer']; + } + if (isset($val['server'])) { + $config['ftp'][$key]['server'] = (string) $val['server']; + } + if (isset($val['timeout'])) { + $config['ftp'][$key]['timeout'] = (int) $val['timeout']; + } + if (isset($val['ssl'])) { + $config['ftp'][$key]['ssl'] = (int) $val['ssl']; + } + if (isset($val['mode'])) { + $config['ftp'][$key]['mode'] = (int) $val['mode']; + } + if (isset($val['port'])) { + $config['ftp'][$key]['port'] = (int) $val['port']; + } + if (isset($val['user'])) { + $config['ftp'][$key]['user'] = (string) $val['user']; + } + if (isset($val['pass'])) { + $config['ftp'][$key]['pass'] = (string) $val['pass']; + } + if (isset($val['dir'])) { + $config['ftp'][$key]['dir'] = (string) $val['dir']; + } + if ($config['ftp'][$key]['dir'] == '' + || (strlen($config['ftp'][$key]['dir']) > 1 + && substr($config['ftp'][$key]['dir'], -1) != '/')) { + $config['ftp'][$key]['dir'] .= '/'; + } + if (isset($_POST['ftp'][$key]['test'])) { + $ftpConnectionCheck[$key] = testFTP($key); + // don't save values - we just want to test the ftp connection data + $saveConfig = false; + } + } +} + +if (isset($_GET['del_ftp'])) { + $index = intval($_GET['del_ftp']); + if (isset($config['ftp'][$index])) { + unset($config['ftp'][$index]); + } + sort($config['ftp']); +} + +$tplConfigurationFtp = new MSDTemplate(); +$tplConfigurationFtp->set_filenames( + array('tplConfigurationFtp' => 'tpl/configuration/ftp.tpl') +); + +$tplConfigurationFtp->assign_vars( + array( + 'ICON_ADD' => $icon['plus'], + 'ICON_DELETE' => $icon['delete'], + 'ICON_SAVE' => $icon['small']['save'] + ) +); + +foreach ($config['ftp'] as $key => $val) { + // if ftp extension is not loaded -> disable ftp transfer + if (!extension_loaded('ftp')) { + $config['ftp'][$key]['transfer'] = 0; + } + + $tplConfigurationFtp->assign_block_vars( + 'FTP', + array( + 'NR' => $key + 1, + 'ID' => $key, + 'FTP_DISABLED' => Html::getDisabled(!extension_loaded("ftp"), true), + 'FTP_ENABLED_SELECTED' => Html::getChecked($val['transfer'], 1), + 'FTP_DISABLED_SELECTED' => Html::getChecked($val['transfer'], 0), + 'FTP_FIELDS_DISABLED' => Html::getDisabled($val['transfer'], 0), + 'FTP_TIMEOUT' => $val['timeout'], + 'FTP_PASSIVE_MODE_SELECTED' => Html::getChecked($val['mode'], 1), + 'FTP_SSL_DISABLED' => + Html::getDisabled(extension_loaded('openssl'), false), + 'FTP_SSL_ENABLED_SELECTED' => Html::getChecked($val['ssl'], 1), + 'FTP_SERVER' => $config['ftp'][$key]['server'], + 'FTP_PORT' => $val['port'], + 'FTP_USER' => Html::replaceQuotes($val['user']), + 'FTP_PASSWORD' => Html::replaceQuotes($val['pass']), + 'FTP_DIRECTORY' => $val['dir'], + 'FTP_CONFIRM_DELETE' => + Html::replaceQuotes($lang['L_FTP_CONFIRM_DELETE']) + ) + ); + if (isset($ftpConnectionCheck[$key])) { + $tplConfigurationFtp->assign_block_vars( + 'FTP.CHECK', array('RESULT' => $ftpConnectionCheck[$key]) + ); + } +} diff --git a/inc/configuration/general.php b/inc/configuration/general.php new file mode 100644 index 0000000..e45d174 --- /dev/null +++ b/inc/configuration/general.php @@ -0,0 +1,146 @@ + delete old log file + $oldlogcompression = $config['logcompression']; + if (isset($_POST['logcompression'])) { + $config['logcompression'] = $_POST['logcompression']; + } else { + $config['logcompression'] = 0; + } + // if zlib-extension is not installed de-activate compression + // for log and dump file automatically + if (!$config['zlib']) { + $config['logcompression'] = 0; + $config['compression'] = 0; + } + // max size of logfiles + if (isset($_POST['log_maxsize1'])) { + $config['log_maxsize1'] = (int) $_POST['log_maxsize1']; + } + if (isset($_POST['log_maxsize2'])) { + $config['log_maxsize2'] = (int) $_POST['log_maxsize2']; + } + if ($config['log_maxsize1'] < 1) { + $config['log_maxsize1'] = 1; + } + if ($config['log_maxsize1'] < 100 && $config['log_maxsize2'] == 1) { + $config['log_maxsize1'] = 100; + } + if ($config['log_maxsize2'] == 1) { + $config['log_maxsize'] = 1024 * $config['log_maxsize1']; // kB + } else { + $config['log_maxsize'] = 1024 * 1024 * $config['log_maxsize1']; // MB + } + if (isset($_POST['empty_db_before_restore'])) { + $config['empty_db_before_restore'] = + (int) $_POST['empty_db_before_restore']; + } + if (isset($_POST['optimize_tables'])) { + $config['optimize_tables_beforedump'] = (int) $_POST['optimize_tables']; + } + //if we are not in mode expert -> reset hidden settings to safe defaults + if ($config['msd_mode'] == 0) { + $config['empty_db_before_restore'] = 0; + } +} + +$tplConfigurationGeneral = new MSDTemplate(); +$tplConfigurationGeneral->set_filenames( + array('tplConfigurationGeneral' => 'tpl/configuration/general.tpl') +); +if ($config['msd_mode'] > 0) { + $tplConfigurationGeneral->assign_block_vars('MODE_EXPERT', array()); +} +$logGz = !$config['zlib'] ? '' : Html::getChecked($config['logcompression'], 1); + +$tplConfigurationGeneral->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'MSD_MODE_EASY_SELECTED' => Html::getChecked($config['msd_mode'], 0), + 'MSD_MODE_EXPERT_SELECTED' => Html::getChecked($config['msd_mode'], 1), + 'GZ_DISABLED' => Html::getDisabled($config['zlib'], 0), + 'LOG_GZ_SELECTED' => $logGz, + 'LOG_MAXSIZE1' => $config['log_maxsize1'], + 'LOG_UNIT_KB_SELECTED' => Html::getSelected($config['log_maxsize2'], 1), + 'LOG_UNIT_MB_SELECTED' => Html::getSelected($config['log_maxsize2'], 2), + 'PHP_MEMORY_LIMIT' => $config['memory_limit'], + 'MIN_SPEED' => $config['minspeed'], + 'MAX_SPEED' => $config['maxspeed'], + 'DUMP_GZ_ENABLED_SELECTED' => + Html::getChecked($config['compression'], 1), + 'DUMP_GZ_DISABLED_SELECTED' => + Html::getChecked($config['compression'], 0), + 'MULTIPART_ENABLED_SELECTED' => + Html::getChecked($config['multi_part'], 1), + 'MULTIPART_DISABLED_SELECTED' => + Html::getChecked($config['multi_part'], 0), + 'MULTIPART_FILE_SIZE' => $config['multipartgroesse1'], + 'MULTIPART_DISABLED' => Html::getDisabled($config['multi_part'], 0), + 'MULTIPART_FILE_SIZE_DISABLED' => + Html::getDisabled($config['multi_part'], 0), + 'MULTIPART_FILE_UNIT_KB_SELECTED' => + Html::getSelected($config['multipartgroesse2'], 1), + 'MULTIPART_FILE_UNIT_MB_SELECTED' => + Html::getSelected($config['multipartgroesse2'], 2), + 'OPTIMIZE_TABLES_ENABLED_SELECTED' => + Html::getChecked($config['optimize_tables_beforedump'], 1), + 'OPTIMIZE_TABLES_DISABLED_SELECTED' => + Html::getChecked($config['optimize_tables_beforedump'], 0), + 'TRUNCATE_DB_ENABLED_SELECTED' => + Html::getChecked($config['empty_db_before_restore'], 1), + 'TRUNCATE_DB_DISABLED_SELECTED' => + Html::getChecked($config['empty_db_before_restore'], 0), + 'STOP_ON_ERROR_ENABLED_SELECTED' => + Html::getChecked($config['stop_with_error'], 1), + 'STOP_ON_ERROR_DISABLED_SELECTED' => + Html::getChecked($config['stop_with_error'], 0) + ) +); + diff --git a/inc/configuration/interface.php b/inc/configuration/interface.php new file mode 100644 index 0000000..cb27346 --- /dev/null +++ b/inc/configuration/interface.php @@ -0,0 +1,100 @@ + $lang['L_POSITION_TL'], + 'tc' => $lang['L_POSITION_TC'], + 'tr' => $lang['L_POSITION_TR'], + 'ml' => $lang['L_POSITION_ML'], + 'mc' => $lang['L_POSITION_MC'], + 'mr' => $lang['L_POSITION_MR'], + 'bl' => $lang['L_POSITION_BL'], + 'bc' => $lang['L_POSITION_BC'], + 'br' => $lang['L_POSITION_BR'] +); + +if (isset($_POST['save'])) { + if (isset($_POST['interface_server_caption'])) { + $config['interface_server_caption'] = 1; + } else { + $config['interface_server_caption'] = 0; + } + if (isset($_POST['interface_server_caption_position_1'])) { + $config['interface_server_caption_position'] = 1; + } else { + $config['interface_server_caption_position'] = 0; + } + if (isset($_POST['sqlboxsize'])) { + $config['interface_sqlboxsize'] = (int) $_POST['sqlboxsize']; + } + if (isset($_POST['theme'])) { + $config['theme'] =(string) $_POST['theme']; + } + // make sure that the theme exists! If not fall back to standard theme + if (!is_dir('./css/' . $config['theme']) + || !is_readable('./css/' . $config['theme'])) { + $config['theme'] = 'msd'; + } + if (isset($_POST['notification_position'])) { + $config['notification_position'] = $_POST['notification_position']; + } + if (isset($_POST['interface_table_compact'])) { + $config['interface_table_compact'] + = (int) $_POST['interface_table_compact']; + }; + if (isset($_POST['resultsPerPage'])) { + $config['resultsPerPage'] = (int) $_POST['resultsPerPage']; + } + if (isset($_POST['refresh_processlist'])) { + $config['refresh_processlist'] = (int) $_POST['refresh_processlist']; + } + if ($config['refresh_processlist'] < 2) { + $config['refresh_processlist'] = 2; + } +} + +$tplConfigurationInterface = new MSDTemplate(); +$tplConfigurationInterface->set_filenames( + array('tplConfigurationInterface' => 'tpl/configuration/interface.tpl') +); + +$tplConfigurationInterface->assign_vars( + array( + 'ICON_SAVE' => $icon['small']['save'], + 'SEL_LANGUAGES' => getLanguageCombo(), + 'LINK_DOWNLOAD_LANGUAGE_PACKS' => + 'http://forum.mysqldumper.de/downloads.php?cat=9', + 'LANGUAGE' => $config['language'], + 'SERVER_CAPTION' => $config['interface_server_caption'], + 'INTERFACE_SERVER_CAPTION_ACTIVATED' => + Html::getChecked($config['interface_server_caption'], 1), + 'INTERFACE_SERVER_CAPTION_DISABLED' => + Html::getDisabled($config['interface_server_caption'], 0), + 'SERVER_CAPTION_POS_MAINFRAME_SELECTED' => + Html::getChecked($config['interface_server_caption_position'], 1), + 'SERVER_CAPTION_POS_MENUE_SELECTED' => + Html::getChecked($config['interface_server_caption_position'], 0), + 'SEL_THEME' => getThemes(), + 'LINK_DOWNLOAD_THEMES' => + 'http://forum.mysqldumper.de/downloads.php?cat=3', + 'SQLBOX_HEIGHT' => intval($config['interface_sqlboxsize']), + 'RESULTS_PER_PAGE' => intval($config['resultsPerPage']), + 'SEL_NOTIFICATION_POSITION' => + Html::getOptionlist($positions, $config['notification_position']), + 'REFRESH_PROCESSLIST' => (int) $config['refresh_processlist'], + 'SQL_GRID_TYPE_COMPACT_SELECTED' => + $config['interface_table_compact'] == 1 ? ' checked="checked"' : '', + 'SQL_GRID_TYPE_NORMAL_SELECTED' => + $config['interface_table_compact'] == 0 ? ' checked="checked"' : '' + ) +); diff --git a/inc/define_icons.php b/inc/define_icons.php new file mode 100644 index 0000000..546f5d1 --- /dev/null +++ b/inc/define_icons.php @@ -0,0 +1,69 @@ + clear arrays with values from last run + $_SESSION['dump'] = array(); + $_SESSION['log'] = array(); + $_SESSION['log']['actions'] = array(); + $_SESSION['log']['errors'] = array(); + $_SESSION['log']['files_created'] = array(); + $dump['comment'] = ''; + $action = 'prepare_dump'; +} +if ($action == 'prepare_dump') include ('./inc/dump/dump_prepare.php'); +if ($action == 'select_tables') include ('./inc/dump/select_tables.php'); +if ($action == 'do_dump') include ('./inc/dump/do_dump.php'); +if ($action == 'done') include ('./inc/dump/dump_finished.php'); +$_SESSION['dump'] = $dump; diff --git a/inc/dump/do_dump.php b/inc/dump/do_dump.php new file mode 100644 index 0000000..061864c --- /dev/null +++ b/inc/dump/do_dump.php @@ -0,0 +1,70 @@ + set start values +$dump = $_SESSION['dump']; +$dump['tables_total'] = 0; +$dump['records_total'] = 0; +$dump['tables_optimized'] = 0; +$dump['part'] = 1; +$dump['part_offset'] = 0; +$dump['errors'] = 0; +//-1 instead of 0 is needed for the execution of command before backup +$dump['table_offset'] = -1; +$dump['table_record_offset'] = 0; +$dump['filename_stamp'] = ''; +$dump['speed'] = $config['minspeed'] > 0 ? $config['minspeed'] : 50; +$dump['max_zeit'] = + (int) $config['max_execution_time'] * $config['time_buffer']; +$dump['dump_start_time'] = time(); +$dump['countdata'] = 0; +$dump['table_offset_total'] = 0; +$dump['page_refreshs'] = 0; +// used as overall flag including e-mail and ftp-actions +$dump['backup_in_progress'] = 1; +// used to determine id databases still need to be dumped +$dump['backup_done'] = 0; +$dump['selected_tables'] = FALSE; +if (isset($_POST['sel_tbl'])) { + $dump['selected_tables'] = $_POST['sel_tbl']; +} +// function was called in dump_prepare +// -- maybe get rid of this second call later on +prepareDumpProcess(); +// last_db_actual is used to detect if db changed in multidump-mode +// -> set to first db +$dump['last_db_actual'] = $dump['db_actual']; + +$_SESSION['config_file'] = $config['config_file']; +$_SESSION['dump'] = $dump; + +$tplDoDump = new MSDTemplate(); +$tplDoDump->set_filenames(array('tplDoDump' => 'tpl/dump/dump.tpl')); +$gzip = $config['compression'] == 1 ? $icon['gz'] : $lang['L_NOT_ACTIVATED']; +$tplDoDump->assign_vars( + array( + 'ICONPATH' => $config['files']['iconpath'], + 'GZIP' => $gzip, + 'SESSION_ID' => session_id(), + 'NOTIFICATION_POSITION' => $config['notification_position'] + ) +); + +$sizeUnits = array(1, 1024, 1024 * 1024, 1024 * 10242 * 1024); +$size = $config['multipartgroesse1'] * $sizeUnits[$config['multipartgroesse2']]; +if ($config['multi_part'] > 0) { + $tplDoDump->assign_block_vars( + 'MULTIPART', array('SIZE' => byteOutput($size)) + ); +} + +$tplDoDump->assign_var('TABLES_TO_DUMP', $dump['tables_total']); + diff --git a/inc/dump/dump_finished.php b/inc/dump/dump_finished.php new file mode 100644 index 0000000..4b88b73 --- /dev/null +++ b/inc/dump/dump_finished.php @@ -0,0 +1,88 @@ +set_filenames( + array('tplDumpFinished' => 'tpl/dump/dump_finished.tpl') +); +$recordsTotal = String::formatNumber((int)$dump['records_total']); +$tablesTotal = $dump['tables_total']; +$msg = sprintf($lang['L_DUMP_ENDERGEBNIS'], $tablesTotal, $recordsTotal); +$tplDumpFinished->assign_vars( + array( + 'ICON_OPEN_FILE' => $icon['small']['open_file'], + 'ICON_EDIT' => $icon['small']['edit'], + 'ICON_VIEW' => $icon['small']['view'], + 'SESSION_ID' => session_id(), + 'BACKUPPATH' => $config['paths']['backup'], + 'PAGE_REFRESHS' => $dump['page_refreshs'], + 'TIME_ELAPSED' => getTimeFormat(time() - $dump['dump_start_time']), + 'MSG' => $msg + ) +); + +if (count($dump['databases']) > 1) { + $msg = sprintf($lang['L_MULTIDUMP_FINISHED'], count($dump['databases'])); + $tplDumpFinished->assign_block_vars('MULTIDUMP', array('MSG' => $msg)); +} +$i = 1; +foreach ($_SESSION['log']['files_created'] as $file) { + $fileSize = @filesize($config['paths']['backup'] . $file); + $tplDumpFinished->assign_block_vars( + 'FILE', + array( + 'NR' => $i, + 'ROWCLASS' => $i % 2 ? 'dbrow' : 'dbrow1', + 'FILENAME' => $file, + 'FILENAME_URLENCODED' => urlencode($file), + 'FILESIZE' => byteOutput($fileSize) + ) + ); + $i++; +} + +$i = 1; +foreach ($_SESSION['log']['actions'] as $message) { + $timestamp = substr($message, 0, 19); + $message = substr($message, 20); + $tplDumpFinished->assign_block_vars( + 'ACTION', + array( + 'NR' => $i, + 'ROWCLASS' => $i % 2 ? 'dbrow' : 'dbrow1', + 'TIMESTAMP' => $timestamp, + 'ACTION' => $message + ) + ); + $i++; +} + +if (sizeof($_SESSION['log']['errors']) > 0) { + $tplDumpFinished->assign_block_vars('ERROR', array()); + $i = 1; + foreach ($_SESSION['log']['errors'] as $error) { + $timestamp = substr($error, 0, 19); + $error = substr($error, 20); + $tplDumpFinished->assign_block_vars( + 'ERROR.ERRORMSG', + array( + 'ROWCLASS' => $i % 2 ? 'dbrow' : 'dbrow1', + 'TIMESTAMP' => $timestamp, + 'MSG' => $error + ) + ); + $i++; + } +} diff --git a/inc/dump/dump_prepare.php b/inc/dump/dump_prepare.php new file mode 100644 index 0000000..e436ab1 --- /dev/null +++ b/inc/dump/dump_prepare.php @@ -0,0 +1,158 @@ + reset hidden settings to safe defaults +if ($config['msd_mode'] == 0) { + $config['replace_command'] = 0; +} + +$tplDumpPrepare = new MSDTemplate(); +$tplDumpPrepare->set_filenames( + array('tplDumpPrepare' => 'tpl/dump/dump_prepare.tpl') +); +if ($config['msd_mode'] > 0) { + $tplDumpPrepare->assign_block_vars('MODE_EXPERT', array()); +} +// set charset-selectbox to standard-encoding of MySQL-Server as initial value +$dump['sel_dump_encoding'] = 'utf8'; +$dump['dump_encoding'] = 'utf8'; +$charsets=$dbo->getCharsets(); +foreach ($charsets as $name=>$val) { + $charsetsDescription[$name]=$name.' - '.$val['Description']; +} +// tables will be selected on next page, but we need a dummy val +// for prepareDumpProcess() +$dump['selected_tables'] = false; +$dbsToBackup = array(); +// look up which databases need to be dumped +prepareDumpProcess(); +$dbs = array_keys($dump['databases']); +$dbsToBackup = implode(', ', $dbs); + +$cext = ($config['cron_extender'] == 0) ? 'pl' : 'cgi'; +$scriptName = $_SERVER['SCRIPT_NAME']; +$serverName = $_SERVER['SERVER_NAME']; +$url = substr($scriptName, 0, strrpos($scriptName, '/') + 1); +if (substr($url, -1) != '/') $url .= '/'; +if (substr($url, 0, 1) != '/') $url = '/' . $url; +$refdir = (substr($config['cron_execution_path'], 0, 1) == '/') ? '' : $url; +$scriptdir = $config['cron_execution_path'] . 'crondump.' . $cext; +$perlModultest = $config['cron_execution_path'] . "perltest.$cext"; +$perlSimpletest = $config['cron_execution_path'] . "simpletest.$cext"; +$scriptentry = myRealpath('./') . $config['paths']['config']; +$cronabsolute = myRealpath('./') . $scriptdir; +if (substr($config['cron_execution_path'], 0, 1) == '/') { + $cronabsolute = $_SERVER['DOCUMENT_ROOT'] . $scriptdir; +} +$confabsolute = $config['config_file']; +$perlHttpCall = getServerProtocol() . $serverName . $refdir; +$perlHttpCall .= $config['cron_execution_path'] . 'crondump.' . $cext; +$perlHttpCall .= '?config=' . $confabsolute; +$perlCrontabCall = 'perl ' . $cronabsolute . ' -config=' . $confabsolute; +$perlCrontabCall .= ' -html_output=0'; +$tplDumpPrepare->assign_vars( + array( + 'SESSION_ID' => session_id(), + 'CONFIG_FILE' => $config['config_file'], + 'POSSIBLE_DUMP_ENCODINGS' => + Html::getOptionlist($charsetsDescription, 'utf8'), + 'DBS_TO_BACKUP' => $dbsToBackup, + 'TABLES_TOTAL' => $dump['tables_total'], + 'RECORDS_TOTAL' => String::formatNumber(intval($dump['records_total'])), + 'DATASIZE_TOTAL' => byteOutput($dump['datasize_total']), + 'NR_OF_DBS' => count($dump['databases']), + 'DUMP_COMMENT' => Html::replaceQuotes($dump['comment']), + 'PERL_TEST' => $perlSimpletest, + 'PERL_MODULTEST' => $perlModultest, + 'PERL_HTTP_CALL' => $perlHttpCall, + 'PERL_CRONTAB_CALL' => $perlCrontabCall, + 'PERL_ABSOLUTE_PATH_OF_CONFIGDIR' => $scriptentry, + 'TIMESTAMP' => time() + ) +); + +if (count($dump['databases']) == 1 && $config['db_actual'] == $dbsToBackup) { + $tplDumpPrepare->assign_block_vars('TABLESELECT', array()); +} +if ($config['compression'] == 1) { + $tplDumpPrepare->assign_block_vars('GZIP_ACTIVATED', array()); +} else { + $tplDumpPrepare->assign_block_vars('GZIP_NOT_ACTIVATED', array()); +} + +if ($config['multi_part'] == 1) { + $tplDumpPrepare->assign_block_vars( + 'MULTIPART', + array('SIZE' => byteOutput($config['multipart_groesse'])) + ); +} else { + $tplDumpPrepare->assign_block_vars('NO_MULTIPART', array()); +} + +if ($config['send_mail'] == 1) { + $tplDumpPrepare->assign_block_vars( + 'SEND_MAIL', + array('RECIPIENT' => $config['email']['recipient_address']) + ); + $recipients = $config['email']['recipient_cc']; + $recipientsCc = implodeSubarray($recipients, 'address'); + if ($recipientsCc > '') { + $tplDumpPrepare->assign_block_vars( + 'SEND_MAIL.CC', array('EMAIL_ADRESS' => $recipientsCc) + ); + } + if ($config['email']['attach_backup'] == 1) { + $bytes = $config['email_maxsize1'] * 1024; + if ($config['email_maxsize2'] == 2) { + $bytes = $bytes * 1024; + } + $tplDumpPrepare->assign_block_vars( + 'SEND_MAIL.ATTACH_BACKUP', array('SIZE' => byteOutput($bytes)) + ); + } else { + $tplDumpPrepare->assign_block_vars( + 'SEND_MAIL.DONT_ATTACH_BACKUP', array() + ); + } +} else { + $tplDumpPrepare->assign_block_vars('NO_SEND_MAIL', array()); +} + +$i = 1; +foreach ($config['ftp'] as $ftp) { + if ($ftp['transfer'] == 1) { + $tplDumpPrepare->assign_block_vars( + 'FTP', + array( + 'NR' => $i, + 'ROWCLASS' => $i % 2 ? 'dbrow1' : 'dbrow' + ) + ); + + $tplDumpPrepare->assign_block_vars( + 'FTP.CONNECTION', + array( + 'SERVER' => $ftp['server'], + 'PORT' => $ftp['port'], + 'DIR' => $ftp['dir'] + ) + ); + $i++; + } +} diff --git a/inc/dump/select_tables.php b/inc/dump/select_tables.php new file mode 100644 index 0000000..da33467 --- /dev/null +++ b/inc/dump/select_tables.php @@ -0,0 +1,76 @@ +set_filenames( + array('tplDumpSelectTables' => 'tpl/dump/selectTables.tpl') + ); + $dbo->selectDb($dump['db_actual']); + $tables=$dbo->getTableStatus(); + $i=0; + foreach ($tables as $tableName => $row) { + $klasse = ($i % 2) ? 1 : ''; + $tableSize = byteOutput($row['Data_length'] + $row['Index_length']); + $tableType = $row['Engine']; + $nrOfRecords = String::formatNumber($row['Rows']); + if (substr($row['Comment'], 0, 4) == 'VIEW' && $row['Engine'] == '') { + $tableType = 'View'; + $tableSize = '-'; + $nrOfRecords = '-'; + } + $tplDumpSelectTables->assign_block_vars( + 'ROW', + array( + 'CLASS' => 'dbrow' . $klasse, + 'ID' => $i, + 'NR' => $i + 1, + 'TABLENAME' => $tableName, + 'TABLETYPE' => $tableType, + 'RECORDS' => $nrOfRecords, + 'SIZE' => $tableSize, + 'LAST_UPDATE' => $row['Update_time'] + ) + ); + $i++; + } + + $tplDumpSelectTables->assign_vars( + array( + 'PAGETITLE' => $lang['L_TABLESELECTION'], + 'SESSION_ID' => session_id(), + 'DATABASE' => $config['db_actual'], + 'ICON_OK' => $icon['ok'], + 'ICON_DELETE' => $icon['delete'], + 'ICON_DB' => $icon['db'], + 'L_NO_MSD_BACKUP' => $lang['L_NOT_SUPPORTED'] + ) + ); +} diff --git a/inc/filemanagement/converter.php b/inc/filemanagement/converter.php new file mode 100644 index 0000000..c66a6f8 --- /dev/null +++ b/inc/filemanagement/converter.php @@ -0,0 +1,77 @@ +pparse('tplGlobalHeader'); +$tplMenu->pparse('tplMenu'); + +// Konverter +$selectfile = (isset($_POST['selectfile'])) ? $_POST['selectfile'] : ''; +$destfile = (isset($_POST['destfile'])) ? $_POST['destfile'] : ''; +$compressed = (isset($_POST['compressed'])) ? $_POST['compressed'] : ''; +include ('./inc/define_icons.php'); +$doConversion = false; +if ($selectfile != '' & file_exists($config['paths']['backup'] . $selectfile) + && strlen($destfile) > 2) { + $doConversion = true; +} + +$tpl = new MSDTemplate(); +$tpl->set_filenames(array('show' => 'tpl/filemanagement/converter.tpl')); + +$tpl->assign_vars( + array( + 'SELECTBOX_FILE_LIST' => getFilelisteCombo($selectfile), + 'NEW_FILE' => Html::replaceQuotes($destfile), + 'NEW_FILE_COMPRESED' => $compressed == 1 ? ' checked="checked"' : '', + 'ICON_GZ' => $icon['gz'] + ) +); + +if ($doConversion) { + $tpl->assign_block_vars('AUTOSCROLL', array()); +} + +$tpl->pparse('show'); +flush(); +if (isset($_POST['startconvert'])) { + echo $lang['L_CONVERTING'] . ' ' . $selectfile + . ' ==> ' . $destfile . '.sql'; + if ($compressed == 1) { + echo '.gz'; + } + if ($doConversion) { + convert($selectfile, $destfile, $compressed); + } else { + echo '

' . $lang['L_CONVERT_WRONG_PARAMETERS'] + . '

'; + } +} + +if ($doConversion) { + ?> + + + + + + '') { + $msg .= $deleteResult; + } +} + +if (isset($_POST['deleteall']) || isset($_POST['deleteallfilter'])) { + if (isset($_POST['deleteall'])) { + $del = deleteMultipartFiles($config['paths']['backup'], '', '.sql'); + $del = array_merge( + $del, deleteMultipartFiles($config['paths']['backup'], '', '.gz') + ); + } else { + $del = deleteMultipartFiles($config['paths']['backup'], $dbactive); + } +} + +// print file-delete-messages +if (is_array($del) && sizeof($del) > 0) { + foreach ($del as $file => $success) { + if ($success) { + $msg .= $lang['L_FM_DELETE1'] . ' \'' . $file . '\' '; + $msg .= $lang['L_FM_DELETE2']; + $msg = Html::getOkMsg($msg); + $log->write(Log::PHP, "Deleted '$file'."); + } else { + $msg .= $lang['L_FM_DELETE1'] . ' \'' . $file . '\' '; + $msg .= $lang['L_FM_DELETE3']; + $msg = Html::getErrorMsg($msg); + $log->write(Log::PHP, "Deleted '$file'."); + } + } +} diff --git a/inc/filemanagement/file_download.php b/inc/filemanagement/file_download.php new file mode 100644 index 0000000..5fb003d --- /dev/null +++ b/inc/filemanagement/file_download.php @@ -0,0 +1,41 @@ +' . $lang['L_FM_UPLOADFAILED'] + ); +} else { + if (!file_exists($config['paths']['backup'] . $_FILES['upfile']['name'])) { + $extension = strrchr($_FILES['upfile']['name'], '.'); + $allowedExtensions = array('.gz', '.sql', 'txt'); + if (!in_array($extension, $allowedExtensions)) { + $msg .= Html::getErrorMsg( + $lang['L_FM_UPLOADNOTALLOWED1'] . '
' + . $lang['L_FM_UPLOADNOTALLOWED2'] + ); + $msg .= Html::getErrorMsg($lang['L_FM_UPLOADFAILED']); + } else { + $upfile = $config['paths']['backup'] . $_FILES['upfile']['name']; + if (@move_uploaded_file($_FILES['upfile']['tmp_name'], $upfile)) { + @chmod($upfile, 0777); + $msg = Html::getOkMsg( + sprintf( + $lang['L_FILE_UPLOAD_SUCCESSFULL'], + $_FILES['upfile']['name'] + ) + ); + } else { + $msg .= Html::getErrorMsg($lang['L_FM_UPLOADMOVEERROR']); + $msg .= Html::getErrorMsg($lang['L_FM_UPLOADFAILED']); + } + } + } else { + $msg .= Html::getErrorMsg($lang['L_FM_UPLOADFILEEXISTS']); + $msg .= Html::getErrorMsg($lang['L_FM_UPLOADFAILED']); + } +} diff --git a/inc/filemanagement/files.php b/inc/filemanagement/files.php new file mode 100644 index 0000000..6e0f085 --- /dev/null +++ b/inc/filemanagement/files.php @@ -0,0 +1,237 @@ +set_filenames(array('tplFiles' => 'tpl/filemanagement/files.tpl')); + +$dbactualOutput = $dbactive; +// replace internal keys for backups of other programs and converted files +// with human readable output +if ($dbactive == '~unknown') { + $dbactualOutput = '' . $lang['L_UNKNOWN'] . ''; +} +if ($dbactive == '~converted') { + $dbactualOutput = '' . $lang['L_CONVERTED_FILES'] . ''; +} +$autoDelete = $lang['L_NOT_ACTIVATED']; +if ($config['auto_delete']['activated'] > 0) { + $autoDelete = $lang['L_ACTIVATED'] . ' (' + . $config['auto_delete']['max_backup_files'] . ' ' + . $lang['L_MAX_BACKUP_FILES_EACH2'] . ')'; +} + +$tplFiles->assign_vars( + array( + 'BACKUP_PATH' => $config['paths']['backup'], + 'ICON_DOWNLOAD' => $icon['open_file'], + 'ICON_VIEW' => $icon['view'], + 'ICON_DELETE' => $icon['delete'], + 'DB_ACTUAL' => $dbactive, + 'DB_ACTUAL_OUTPUT' => $dbactualOutput, + 'UPLOAD_MAX_SIZE' => $config['upload_max_filesize'], + 'AUTODELETE_ENABLED' => $autoDelete, + 'NOTIFICATION_POSITION' => $config['notification_position'] + ) +); + +if ($msg > '') { + $tplFiles->assign_block_vars( + 'MESSAGE', array('TEXT' => Html::getJsQuote($msg, true)) + ); +} +$backups = getBackupfileInfo(); +$i = 0; +if (!isset($backups['databases'][$dbactive])) { + $tplFiles->assign_block_vars('NO_FILE_FOUND', array()); +} +if ($dbactive != '~unknown' && $dbactive != '~converted') { + $tplFiles->assign_block_vars('DELETE_FILTER', array()); +} +// show detailed file info of the selected database at top +foreach ($backups['files'] as $backup) { + if ($backup['db'] == $dbactive) { + // get MySQL Version from which the backup was taken + $mysqlVersion = ''; + if (isset($backup['mysqlversion'])) { + $mysqlVersion = $backup['mysqlversion']; + } + // get grouping name of database + $dbName = $backup['name']; + if (!in_array($backup['db'], array('~unknown', '~converted'))) { + $dbName = $backup['db']; + } + // with which MSD-Version was the backup made? + $scriptVersion = $lang['L_UNKNOWN']; + if ($backup['script'] > '') { + $scriptVersion = $backup['script']; + } + // show Gzip-Icon? + $compressed = substr($backup['name'], -3) == '.gz' ? $icon['gz'] : '-'; + // is a commetn given? + $comment = ''; + if ($backup['comment'] > '') { + $comment = nl2br(wordwrap($backup['comment'], 50)); + } + // number of tables + $nrOfTables = $lang['L_UNKNOWN']; + if ($backup['tables'] > -1) { + $nrOfTables = String::formatNumber($backup['tables']); + } + // number of records + $nrOfRecords = $lang['L_UNKNOWN']; + if ($backup['records'] > -1) { + $nrOfRecords = String::formatNumber($backup['records']); + } + // charset of bakup file + $charset = $lang['L_UNKNOWN']; + if ($backup['charset'] != '?') { + $charset = $backup['charset']; + } + + $tplFiles->assign_block_vars( + 'FILE', + array( + 'ROWCLASS' => $i % 2 ? 'dbrow' : 'dbrow1', + 'FILE_INDEX' => $i, + // expand or unexpand multipart list on next click + 'FILE_EXPAND_INDEX' => $expand == $i ? -1 : $i, + 'FILE_NAME' => $backup['name'], + 'FILE_NAME_URLENCODED' => urlencode($backup['name']), + 'DB_NAME' => $dbName, + 'ICON_COMPRESSED' => $compressed, + 'SCRIPT_VERSION' => $scriptVersion, + 'COMMENT' => $comment, + 'FILE_CREATION_DATE' => $backup['date'], + 'NR_OF_TABLES' => $nrOfTables, + 'NR_OF_RECORDS' => $nrOfRecords, + 'FILESIZE' => byteOutput($backup['size']), + 'FILE_CHARSET' => $charset, + 'NR_OF_MULTIPARTS' => $backup['multipart'], + 'MYSQL_VERSION' => $mysqlVersion + ) + ); + + if ($backup['multipart'] > 0) { + $mpFileHeadline = $lang['L_FILES']; + if ($backup['multipart'] == 1) { + $mpFileHeadline = $lang['L_FILE']; + } + $tplFiles->assign_block_vars( + 'FILE.IS_MULTIPART', array('FILES' => $mpFileHeadline) + ); + } else { + $tplFiles->assign_block_vars('FILE.NO_MULTIPART', array()); + } + if ($backup['multipart'] > 0) { + // show all files of a Multipart-backup + $tplFiles->assign_block_vars( + 'FILE.EXPAND_MULTIPART', + array( + 'NR' => $i, + 'ROWCLASS' => $i % 2 ? 'dbrow1' : 'dbrow' + ) + ); + // expand multipartlist if click came from restore screen + if ($expand == $i) { + $tplFiles->assign_block_vars( + 'EXPAND_MP_FILE', array('FILEINDEX' => $i) + ); + } + + $partPosition = strrpos($backup['name'], 'part_'); + $fileBase = substr($backup['name'], 0, $partPosition) . 'part_'; + $fileExtension = '.sql'; + if (substr($backup['name'], -2) == 'gz') { + $fileExtension = '.sql.gz'; + } + for ($x = 0; $x < $backup['multipart']; $x++) { + $fileName = $fileBase . ($x + 1) . $fileExtension; + $fileSize = $lang['L_UNKNOWN']; + $file = $config['paths']['backup'] . $fileName; + if (!is_readable($file)) { + $fileName = $backup['name']; + $fileSize = byteOutput(@filesize($file)); + } + $tplFiles->assign_block_vars( + 'FILE.EXPAND_MULTIPART.MP_FILE', + array( + 'ROWCLASS' => $x % 2 ? 'dbrow' : 'dbrow1', + 'NR' => $x + 1, + 'FILE_NAME' => $fileName, + 'FILE_NAME_URLENCODED' => urlencode($fileName), + 'FILE_SIZE' => $fileSize + ) + ); + } + } + $i++; + } +} + +//sort databases according to the databasenames ascending +ksort($backups['databases']); +// list summary of other backup files grouped by databases +if (count($backups['databases']) > 0) { + $i = 0; + foreach ($backups['databases'] as $db => $info) { + $rowclass = $i % 2 ? 'dbrow' : 'dbrow1'; + if ($db == $dbactive) { + $rowclass = 'dbrowsel'; + } + $dbNameOutput = $db; + if ($db == '~unknown') { + $dbNameOutput = '' . $lang['L_NO_MSD_BACKUPFILE'] . ''; + } + if ($db == '~converted') { + $dbNameOutput = '' . $lang['L_CONVERTED_FILES'] . ''; + } + + $nrOfBackups = isset($info['backup_count']) ? $info['backup_count'] : 0; + $latestBackup = '-'; + if (isset($info['latest_backup_timestamp'])) { + $latestBackup = $info['latest_backup_timestamp']; + } + $fileSizeTotal = 0; + if (isset($info['backup_size_total'])) { + $fileSizeTotal = byteOutput($info['backup_size_total']); + } + + $tplFiles->assign_block_vars( + 'DB', + array( + 'ROWCLASS' => $rowclass, + 'DB_NAME_LINK' => $db, + 'DB_NAME' => $dbNameOutput, + 'NR_OF_BACKUPS' => $nrOfBackups, + 'LATEST_BACKUP' => $latestBackup, + 'SUM_SIZE' => $fileSizeTotal + ) + ); + $i++; + } +} + +$tplFiles->assign_vars( + array( + 'SUM_SIZE' => byteOutput($backups['filesize_total']), + 'FREESPACE_ON_SERVER' => getFreeDiskSpace() + ) +); diff --git a/inc/files.php b/inc/files.php new file mode 100644 index 0000000..aea505e --- /dev/null +++ b/inc/files.php @@ -0,0 +1,37 @@ += 1024; $level++) { + $bytes /= 1024; + } + switch ($level) + { + case 0: + $suffix = 'B'; + break; + case 1: + $suffix = 'KB'; + break; + case 2: + $suffix = 'MB'; + break; + case 3: + $suffix = 'GB'; + break; + case 4: + $suffix = 'TB'; + break; + case 5: + $suffix = 'PB'; + break; + case 6: + $suffix = 'EB'; + break; + case 7: + $suffix = 'ZB'; + break; + + default: + $suffix = ''; + break; + } + if (!$useHTML) { + $suffix = strip_tags($suffix); + } + $ret = sprintf("%01." . $precision . "f", round($bytes, $precision)); + return $ret . ' ' . $suffix; +} + +/** + * Delete Multipart files + * + * @param string $dir Directory to check + * @param string $prefix Only check files with this prefix + * @param string $suffix Only check files with this suffix + * + * @return array Array $file['filename']=true | false + */ +function deleteMultipartFiles($dir, $prefix = '', $suffix = '') +{ + $deleted = array(); + if (substr($dir, -1) != '/') { + $dir .= '/'; + } + if (is_dir($dir)) { + $d = opendir($dir); + while ($file = readdir($d)) { + if (is_file($dir . $file)) { + $del = false; + if ($prefix > '' && substr($file, 0, strlen($prefix)) == $prefix) $del = true; + if ($suffix > '' && substr($file, strlen($file) - strlen($suffix), strlen($suffix)) == $suffix) $del = true; + if ($del) + { + if (unlink($dir . $file)) $deleted[$file] = true; + else $deleted[$file] = false; + } + } + } + closedir($d); + return $deleted; + } +} + +/** + * Add a database to the global $databases-Array and set necessary indexes + * + * @param string $db_name Databaase-Name + * @param string $praefix Table-Prefix + * @param string $cbd Command before Dump + * @param string $cad Command after Dump + * + * @return void + */ +function addDatabaseToConfig($db_name) +{ + global $databases; + if (!isset($databases)) $databases = array(); + if (!isset($databases[$db_name])) $databases[$db_name] = array(); + if (!isset($databases[$db_name]['dump'])) $databases[$db_name]['dump'] = 0; + if (!isset($databases[$db_name]['prefix'])) $databases[$db_name]['prefix'] = ''; + if (!isset($databases[$db_name]['command_before_dump'])) $databases[$db_name]['command_before_dump'] = ''; + if (!isset($databases[$db_name]['command_after_dump'])) $databases[$db_name]['command_after_dump'] = ''; +} + +/** + * Check settings of language, config file, reloads list of databases and save result to configuration + * + * @return string String with checked and added databases + */ +function setDefaultConfig() +{ + global $config, $databases, $out, $lang, $dbo; + + // check language and fallback to englisch if language file is not readable + $lang_file = './language/' . $config['language'] . '/lang.php'; + if (!file_exists($lang_file) || !is_readable($lang_file)) + { + $config['language'] = 'en'; + } + include ('./language/' . $config['language'] . '/lang.php'); + + getConfig($config['config_file']); // falls back to config mysqldumper if something is wrong + // get list of databases for this user + $dbUser = $dbo->getDatabases(); + foreach ($dbUser as $db) + { + // new found db? -> add it + if (!isset($databases[$db])) $databases[$db] = array(); + } + ksort($databases); + foreach ($databases as $db_name => $val) + { + if ($dbo->selectDb($db_name, true)) + { + addDatabaseToConfig($db_name); + $out .= $lang['L_SAVING_DB_FORM'] . " " . $db_name . " " . $lang['L_ADDED'] . "\n"; + } + else + unset($databases[$db_name]); + } + saveConfig(); + return $out; +} + +/** + * Save actual configuration to file + * + * @return boolean + */ +function saveConfig() +{ + global $config, $databases, $configDontsave; + + $config['multipart_groesse'] = $config['multipartgroesse1'] * (($config['multipartgroesse2'] == 1) ? 1024 : 1024 * 1024); + if (!isset($config['email_maxsize'])) $config['email_maxsize'] = $config['email_maxsize1'] * (($config['email_maxsize2'] == 1) ? 1024 : 1024 * 1024); + if (!isset($config['cron_execution_path'])) $config['cron_execution_path'] = "msd_cron/"; + + $config2 = $config; + foreach ($config2 as $var => $val) + { + if (in_array($var, $configDontsave)) unset($config2[$var]); + } + + $t = '$config=array_merge($config,unserialize(base64_decode(\'' . base64_encode(serialize($config2)) . "')));\r\n"; + if (isset($databases)) $t .= '$databases=array_merge($databases,unserialize(base64_decode(\'' . base64_encode(serialize($databases)) . "')));\r\n"; + + $pars_all = '"; + + $ret = true; + $file = './' . $config['paths']['config'] . $config['config_file'] . '.php'; + if ($fp = @fopen($file, "wb")) + { + if (!fwrite($fp, $pars_all)) $ret = false; + if (!fclose($fp)) $ret = false; + @chmod($file, 0777); + } + else + $ret = false; + if ($ret) + { + $_SESSION['config'] = $config; + $ret = writeCronScript(); + } + return $ret; +} + + +/** + * Build string of array according to Perl syntax (needed to save Perl configuration file) + * + * @param array $arr Array to build the string from + * @param string $mode 0 for strings, 1 for int values + * + * @return string The converted Perl string + */ +function myImplode($arr, $mode = 0) // 0=String, 1=intval +{ + if (!is_array($arr)) return false; + foreach ($arr as $key => $val) + { + if ($mode == 0) $arr[$key] = Html::escapeSpecialchars($val); + else $arr[$key] = intval($val); + } + if ($mode == 0) $ret = '("' . implode('","', $arr) . '")'; + else $ret = '(' . implode(',', $arr) . ')'; + return $ret; +} + +/** + * Build and save the actual configuration file for Perl (used by crondump.pl) + * + * @return boolean true on success or false on failure + */ +function writeCronScript() +{ + global $config, $databases, $cron_db_array, $cron_dbpraefix_array, $cron_db_cbd_array, $cron_db_cad_array; + if (!isset($config['email_maxsize'])) $config['email_maxsize'] = $config['email_maxsize1'] * (($config['email_maxsize2'] == 1) ? 1024 : 1024 * 1024); + if (isset($config['db_actual'])) $cron_dbname = $config['db_actual']; + else + { + //get first database name from database-array (this is the case at fresh installing) + $dbs = array_keys($databases); + $cron_dbname = $dbs[0]; + } + // -2 = Multidump configuration + // -3 = all databases - nothing to do + // get standard values for all databases + $cron_db_array = ''; //$databases['Name']; + $cron_dbpraefix_array = ''; //$databases['praefix']; + $cron_command_before_dump = ''; //$databases['command_before_dump']; + $cron_command_after_dump = ''; //$databases['command_after_dump']; + + + $cron_ftp_server = ''; + $cron_ftp_port = ''; + $cron_ftp_mode = ''; + $cron_ftp_user = ''; + $cron_ftp_pass = ''; + $cron_ftp_dir = ''; + $cron_ftp_timeout = ''; + $cron_ftp_ssl = ''; + $cron_ftp_transfer = ''; + + //build db-arrays + foreach ($databases as $k => $v) + { + //should we dump this database + if (isset($databases[$k]['dump']) && $databases[$k]['dump'] === 1) + { + $cron_db_array[] .= $k; + $cron_dbpraefix_array[] .= $databases[$k]['prefix']; + $cron_command_before_dump[] .= $databases[$k]['command_before_dump']; + $cron_command_after_dump[] .= $databases[$k]['command_after_dump']; + } + } + + //build ftp-arrays + foreach ($config['ftp'] as $k => $v) + { + $cron_ftp_server[] .= $config['ftp'][$k]['server']; + $cron_ftp_port[] .= $config['ftp'][$k]['port']; + $cron_ftp_mode[] .= $config['ftp'][$k]['mode']; + $cron_ftp_user[] .= $config['ftp'][$k]['user']; + $cron_ftp_pass[] .= $config['ftp'][$k]['pass']; + $cron_ftp_dir[] .= $config['ftp'][$k]['dir']; + $cron_ftp_timeout[] .= $config['ftp'][$k]['timeout']; + $cron_ftp_ssl[] .= $config['ftp'][$k]['ssl']; + $cron_ftp_transfer[] .= $config['ftp'][$k]['transfer']; + } + + $r = str_replace("\\\\", "/", $config['paths']['root']); + $r = str_replace("@", "\@", $r); + + //built recipient_cc-arrays + $recipients_cc = ''; + foreach ($config['email']['recipient_cc'] as $k => $v) + { + $recipients_cc .= '"' . $config['email']['recipient_cc'][$k]['name'] . '" <' . $config['email']['recipient_cc'][$k]['address'] . '>, '; + } + $recipients_cc = substr($recipients_cc, 0, -2); + + // auf manchen Server wird statt 0 ein leerer String gespeichert -> fuehrt zu einem Syntax-Fehler + // hier die entsprechenden Ja/Nein-Variablen sicherheitshalber in intvalues aendern + $int_array = array( + 'dbport', + 'cron_compression', + 'cron_printout', + 'multi_part', + 'multipart_groesse', + 'email_maxsize', + //'auto_delete][activated', + //'auto_delete][max_backup_files', + 'perlspeed', + 'optimize_tables_beforedump', + 'logcompression', + 'log_maxsize', + 'cron_completelog', + 'use_sendmail', + 'smtp_port', + 'smtp_useauth', + 'smtp_usessl'); + + foreach ($int_array as $i) + { + if (is_array($i)) + { + foreach ($i as $key => $val) + { + $int_array[$key] = intval($val); + } + } + else + { + if (!isset($config[$i])) $config[$i] = 0; + $config[$i] = intval($config[$i]); + } + } + if ($config['dbport'] == 0) $config['dbport'] = 3306; + + $cronscript = "') . '";' . "\n"; + $cronscript .= '$cronmailto_cc="' . Html::escapeSpecialchars($recipients_cc) . '";' . "\n"; + $cronscript .= '$cronmailfrom="' . Html::escapeSpecialchars('"' . $config['email']['sender_name'] . '" <' . $config['email']['sender_address'] . '>') . '";' . "\n"; + $cronscript .= '$cron_use_sendmail=' . $config['use_sendmail'] . ';' . "\n"; + $cronscript .= '$cron_smtp="' . Html::escapeSpecialchars($config['smtp_server']) . '";' . "\n"; + $cronscript .= '$smtp_port=' . $config['smtp_port'] . ';' . "\n"; + $cronscript .= '$smtp_useauth=' . $config['smtp_useauth'] . ';' . "\n"; + $cronscript .= '$smtp_usessl=' . $config['smtp_usessl'] . ';' . "\n"; + $cronscript .= '$smtp_user="' . $config['smtp_user'] . '";' . "\n"; + $cronscript .= '$smtp_pass="' . $config['smtp_pass'] . '";' . "\n"; + $cronscript .= '@cron_db_array=' . myImplode($cron_db_array) . ';' . "\n"; + $cronscript .= '@cron_dbpraefix_array=' . myImplode($cron_dbpraefix_array) . ';' . "\n"; + $cronscript .= '@cron_command_before_dump=' . myImplode($cron_command_before_dump) . ';' . "\n"; + $cronscript .= '@cron_command_after_dump=' . myImplode($cron_command_after_dump) . ';' . "\n"; + + $cronscript .= '@ftp_server=' . myImplode($cron_ftp_server) . ';' . "\n"; + $cronscript .= '@ftp_port=' . myImplode($cron_ftp_port, 1) . ';' . "\n"; + $cronscript .= '@ftp_mode=' . myImplode($cron_ftp_mode, 1) . ';' . "\n"; + $cronscript .= '@ftp_user=' . myImplode($cron_ftp_user) . ';' . "\n"; + $cronscript .= '@ftp_pass=' . myImplode($cron_ftp_pass) . ';' . "\n"; + $cronscript .= '@ftp_dir=' . myImplode($cron_ftp_dir) . ';' . "\n"; + $cronscript .= '@ftp_timeout=' . myImplode($cron_ftp_timeout, 1) . ';' . "\n"; + $cronscript .= '@ftp_useSSL=' . myImplode($cron_ftp_ssl, 1) . ';' . "\n"; + $cronscript .= '@ftp_transfer=' . myImplode($cron_ftp_transfer, 1) . ';' . "\n"; + + $cronscript .= '$mp=' . $config['multi_part'] . ';' . "\n"; + $cronscript .= '$multipart_groesse=' . $config['multipart_groesse'] . ';' . "\n"; + $cronscript .= '$email_maxsize=' . $config['email_maxsize'] . ';' . "\n"; + $cronscript .= '$auto_delete=' . $config['auto_delete']['activated'] . ';' . "\n"; + $cronscript .= '$max_backup_files=' . $config['auto_delete']['max_backup_files'] . ';' . "\n"; + $cronscript .= '$perlspeed=' . $config['perlspeed'] . ';' . "\n"; + $cronscript .= '$optimize_tables_beforedump=' . $config['optimize_tables_beforedump'] . ';' . "\n"; + $cronscript .= '$logcompression=' . $config['logcompression'] . ';' . "\n"; + + //add .gz to logfiles? + if ($config['logcompression'] === 1) + { + $cronscript .= '$logdatei="' . $config['paths']['root'] . $config['files']['perllog'] . '.gz";' . "\n"; + $cronscript .= '$completelogdatei="' . $config['paths']['root'] . $config['files']['perllogcomplete'] . '.gz";' . "\n"; + } + else + { + $cronscript .= '$logdatei="' . $config['paths']['root'] . $config['files']['perllog'] . '";' . "\n"; + $cronscript .= '$completelogdatei="' . $config['paths']['root'] . $config['files']['perllogcomplete'] . '";' . "\n"; + } + $cronscript .= '$log_maxsize=' . $config['log_maxsize'] . ';' . "\n"; + $cronscript .= '$complete_log=' . $config['cron_completelog'] . ';' . "\n"; + $cronscript .= '$cron_comment="' . Html::escapeSpecialchars(stripslashes($config['cron_comment'])) . '";' . "\n"; + $cronscript .= "?>"; + + // Save config + $ret = true; + $sfile = './' . $config['paths']['config'] . $config['config_file'] . '.conf.php'; + if (file_exists($sfile)) @unlink($sfile); + + if ($fp = @fopen($sfile, "wb")) + { + if (!fwrite($fp, $cronscript)) $ret = false; + if (!fclose($fp)) $ret = false; + @chmod("$sfile", 0777); + } + else + $ret = false; + + // if standard config was deleted -> restore it with the actual values + if (!file_exists('./' . $config['paths']['config'] . "mysqldumper.conf.php")) + { + $sfile = './' . $config['paths']['config'] . 'mysqldumper.conf.php'; + if ($fp = fopen($sfile, "wb")) + { + if (!fwrite($fp, $cronscript)) $ret = false; + if (!fclose($fp)) $ret = false; + @chmod("$sfile", 0777); + } + else + $ret = false; + } + return $ret; +} + +/** + * Collect log file information and return it as assoziative array + * + * @param boolean $logcompression Watch for compressed (true) or uncompressed files + * + * @return array Associative array with information + */ +function getLogFileInfo($logcompression) +{ + global $config; + + $l = Array(); + $sum = $s = $l['log_size'] = $l['perllog_size'] = $l['perllogcomplete_size'] = $l['errorlog_size'] = $l['log_totalsize'] = 0; + if ($logcompression == 1) + { + $l['log'] = $config['files']['log'] . ".gz"; + $l['perllog'] = $config['files']['perllog'] . ".gz"; + $l['perllogcomplete'] = $config['files']['perllogcomplete'] . ".gz"; + $l['errorlog'] = $config['paths']['log'] . "error.log.gz"; + } + else + { + $l['log'] = $config['files']['log']; + $l['perllog'] = $config['files']['perllog']; + $l['perllogcomplete'] = $config['files']['perllogcomplete']; + $l['errorlog'] = $config['paths']['log'] . "error.log"; + } + $l['log_size'] += @filesize($l['log']); + $sum += $l['log_size']; + $l['perllog_size'] += @filesize($l['perllog']); + $sum += $l['perllog_size']; + $l['perllogcomplete_size'] += @filesize($l['perllogcomplete']); + $sum += $l['perllogcomplete_size']; + $l['errorlog_size'] += @filesize($l['errorlog']); + $sum += $l['errorlog_size']; + $l['log_totalsize'] += $sum; + + return $l; +} + +/** + * Delete log file and recreates it. + * + * @return void + */ +function deleteLog() +{ + global $config; + $log = date('d.m.Y H:i:s') . " Log created.\n"; + if (file_exists($config['files']['log'] . '.gz')) @unlink($config['files']['log'] . '.gz'); + if (file_exists($config['files']['log'] . '.gz')) @unlink($config['files']['log']); + if ($config['logcompression'] == 1) + { + $fp = @gzopen($config['files']['log'] . '.gz', "wb"); + @gzwrite($fp, $log); + @gzclose($fp); + @chmod($config['files']['log'] . '.gz', 0777); + } + else + { + $fp = @fopen($config['files']['log'], "wb"); + @fwrite($fp, $log); + @fclose($fp); + @chmod($config['files']['log'], 0777); + } +} + +/** + * Detect accessable databases for the current SQL-User in $config-array and returns output-string with all dbs + * Additionally it adds all found databases in the global var $databases + * + * @param boolean $printout Wether to return the output string or not + * @param string $db Optional name of a database to add manually + * + * @return string Output string containing all found dbs + */ +function searchDatabases($printout = 0, $db = '') +{ + global $config, $lang, $dbo, $databases; + $databases = array(); + $ret = ''; + $db_list = $dbo->getDatabases(); + // add manual added db to array, but only if it was not detected before + if ($db > '' && !in_array($db, $db_list)) $db_list[] = $db; + // now check if we can select the db - if not, we can't access the database + if (sizeof($db_list) > 0) + { + foreach ($db_list as $db) + { + $res = $dbo->selectDb($db, true); + + if ($res === true) + { + addDatabaseToConfig($db); + if ($printout == 1) $ret .= Html::getOkMsg($lang['L_FOUND_DB'] . ' `' . $db); + } elseif ($printout == 1) { + $ret .= Html::getErrorMsg($lang['L_ERROR'].' : '.$res); + } + } + } + return $ret; +} + +/** + * realpath implementation working on any server + * + * @return $dir string The application path + */ +function myRealpath() +{ + $dir = dirname(__FILE__); + $dir = str_replace('\\', '/', $dir); + $dir = str_replace('//', '/', $dir); + if (substr($dir, -14) == '/inc/functions') $dir = substr($dir, 0, -13); + if (substr($dir, -1) != '/') $dir .= '/'; + return $dir; +} + +/** + * Remove tags recursivly from array or from string + * + * @param $value string | array Value/s to strip + * + * @return string|array Cleaned values + */ +function myStripTags($value) +{ + global $dont_strip; + if (is_array($value)) + { + foreach ($value as $key => $val) + { + if (!in_array($key, $dont_strip)) $ret[$key] = myStripTags($val); + else $ret[$key] = $val; + } + } + else + $ret = trim(strip_tags($value)); + return $ret; +} + +/** + * First start output buffering then start SESSION + * Reads configuration and main-language file lang.php and creates + * Database-Object $dbo + * + * @param string $json Return JSON-Encoded answer + * @param string $send_header If set to false headers are completely skipped + * @return void + */ +function obstart($json = false, $send_header = true) +{ + global $dbo, $config, $databases, $dump, $lang; + if ($config['ob_gzhandler']) + { + if (!@ob_start("ob_gzhandler")) @ob_start(); + } + + // if default config file doesn't exists, it is a new installation -> redirect to installation + if (!$json && !file_exists('./work/config/mysqldumper.php')) + { + header("location: install.php"); + die(); + exit(); + } + + session_name('MySQLDumper'); + $res = session_start(); + if (false === $res) die("Error starting session! Check server."); + if (isset($_SESSION['config_file'])) $config['config_file'] = $_SESSION['config_file']; + else $config['config_file'] = 'mysqldumper'; + if ($send_header) + { + header('Pragma: no-cache'); + header('Cache-Control: no-cache, must-revalidate'); // HTTP/1.1 + header('Expires: -1'); // Datum in der Vergangenheit + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Cache-Control: post-check=0, pre-check=0', false); + header('Last-Modified: ' . gmdate("D, d M Y H:i:s") . ' GMT'); + if (!$json) header('Content-Type: text/html; charset=UTF-8'); + else header('Content-type: application/x-json'); + } + // get config from configuration file if not set + if (!isset($_SESSION['config'])) getConfig($config['config_file']); + else + { + // otherwise get parameters from session + $config = array_merge($config, $_SESSION['config']); + if (isset($_SESSION['databases'])) $databases = $_SESSION['databases']; + if (isset($_SESSION['dump'])) $dump = $_SESSION['dump']; + } + // special case -> configuration is set to a language that was deleted meanwhile + if (!is_readable('./language/' . $config['language'] . '/lang.php')) $config['language'] = 'en'; + + include ('./language/' . $config['language'] . '/lang.php'); + // create database object + $dbo = MsdDbFactory::getAdapter($config['dbhost'], $config['dbuser'], $config['dbpass'], $config['dbport'], $config['dbsocket']); + if (!isset($_SESSION['databases'])) setDefaultConfig(); +//echo $config['db_actual']; +// die(); + if (isset($config['db_actual']) && $config['db_actual'] > '') $dbo->selectDb($config['db_actual']); + else + { + if (!isset($databases)) { + // no config loaded -> SetDefault-Values + setDefaultConfig(); + } + // get first DB-Name and set as actual db + $dbNames = array_keys($databases); + $config['db_actual'] = $dbNames[0]; + $dbo->selectDb($config['db_actual']); + } + //$_SESSION['config'] = $config; + //$_SESSION['databases'] = $databases; +} + +/** + * Add end of body/html, end output buffering and output the buffer + * + * @param boolean $ajax + * @return void + */ +function obend($ajax = false) +{ + global $config, $databases; + $_SESSION['databases'] = $databases; + $_SESSION['config'] = $config; + if (!$ajax) { + echo '' . "\n\n" . '' . "\n\n" . ''; + // close HTML-page + } + @ob_end_flush(); +} + +/** + * Extract unique prefixes from an array + * + * and return new array containing the different prefixes + * + * @param array $array Array to scan for prefixes + * @param boolean $addNoneOption Wether to add a first entry '---' + * + * @return $prefix_array array The array conatining the unique prefixes + */ +function getPrefixArray($array, $addNoneOption = true) +{ + $prefixes = array(); + $keys = array_keys($array); + foreach ($keys as $k) { + $pos = strpos($k, '_'); // find '_' + if ($pos !== false) { + $prefix = substr($k, 0, $pos); + if (!in_array($prefix, $prefixes)) { + $prefixes[$prefix] = $prefix; + } + } + } + if ($addNoneOption) { + $prefixes['-1'] = '---'; + } + ksort($prefixes); + return $prefixes; +} + +/** + * Implode the given keys of multidimensional array using the implode string + * + * @param array $array The array to implode + * @param string $key The values that should be imploded + * @param string $implodeString The string to concatenate the values with + * + * @return string The impoloded valuies as string + */ +function implodeSubarray($array, $key, $implodeString = ', ') +{ + $ret = ''; + foreach ($array as $k => $v) { + $ret .= $v[$key] . $implodeString; + } + if (strlen($ret) > 1) { + $ret = substr($ret, 0, -(strlen($implodeString))); + } + return $ret; +} + diff --git a/inc/functions/functions_dump.php b/inc/functions/functions_dump.php new file mode 100644 index 0000000..d8f9ec1 --- /dev/null +++ b/inc/functions/functions_dump.php @@ -0,0 +1,668 @@ +write( + Log::PHP, + sprintf( + $lang['L_SAVING_DATA_TO_FILE'], + $dump['db_actual'], + $dump['backupdatei'] + ) + ); + if ($dump['part'] == 1) { + $dump['table_offset'] = 0; + $dump['countdata'] = 0; + } + // Seitenerstaufruf -> Backupdatei anlegen + $dump['data'] = $statuszeile . '-- Dump created: ' . $curTime; + } else { + if ($config['multi_part'] != 0) { + $log->write( + Log::PHP, + sprintf( + $lang['L_SAVING_DATA_TO_MULTIPART_FILE'], + $dump['backupdatei'] + ) + ); + $dump['data'] = $statuszeile . '-- ' . ' This is part ' + . ($dump['part'] - $dump['part_offset']) . ' of the backup.' + . "\n\n" . $dump['data']; + } + } + writeToDumpFile(); + $dump['part']++; +} + +/** + * Creates the first statusline with information for backup files + * + * @return string + */ +function getStatusLine() +{ + // strcuture of status line: + // -- Status:nrOfTables:nrOfRecords:Multipart:database:script: + // scriptversion:comment:MySQL-Version:Backupflags(unused):SQLBefore: + //SQLAfter:Charset:CharsetEXTINFO + + global $databases, $config, $dump; + if (!defined('MSD_MYSQL_VERSION')) { + GetMySQLVersion(); + } + $tline = "-- \n-- TABLE-INFO\r\n"; + + foreach ($dump['databases'][$dump['db_actual']]['tables'] + as $tableName => $val) { + $tline .= "-- TABLE|" . $tableName . '|' . $val['records'] + . '|' . $val['data_length'] . '|' . $val['update_time'] . '|' + . $val['engine'] . "\n"; + } + $flags = 1; + $mp = 'MP_0'; + if ($config['multi_part'] == 1) { + $mp = "MP_" . ($dump['part'] - $dump['part_offset']); + } + $statusline = "-- Status:" + . $dump['databases'][$dump['db_actual']]['table_count'] + . ':' . $dump['databases'][$dump['db_actual']]['records_total'] + . ":$mp:" . $dump['db_actual'] . ":PHP:" . MSD_VERSION . ":" + . $dump['comment'] . ":"; + $statusline .= MSD_MYSQL_VERSION . ":$flags:::" . $dump['dump_encoding'] + . ":EXTINFO\n" . $tline . "-- " . "EOF TABLE-INFO\n-- "; + return $statusline; +} + +/** + * Build the DROP and CREATE TABLE string for dump file. + * + * Parameter $withdata decides if we should add the + * "ALTER TABLE DISABLE KEYS"-query. + * + * @param string $db The database + * @param string $table The table + * @param integer $withdata Add DISABLE KEYS + * @return string $def Created Query-string or false on error + */ +function getCreateString($db, $table, $withdata = 1) +{ + global $dbo, $config, $dump, $lang, $log; + + $def = "\n\n--\n-- Table structure for table `$table`\n--\n"; + if ($dump['databases'][$dump['db_actual']]['tables'][$table]['engine'] + == 'VIEW') { + $def .= "DROP VIEW IF EXISTS `$table`;\n"; + $withdata = 0; + } else { + $def .= "DROP TABLE IF EXISTS `$table`;\n"; + } + $createStatement = $dbo->getTableCreate($table, $db); + $def .= $createStatement . ';' . "\n\n"; + if ($withdata == 1) { + $def .= "--\n-- Dumping data for table `$table`\n--\n"; + $def .= "/*!40000 ALTER TABLE `$table` DISABLE KEYS */;\n"; + } + $log->write(Log::PHP, sprintf($lang['L_CREATE_TABLE_SAVED'], $table)); + return $def; +} + +/** + * Read records from table, build query-strings and write them to dump file + * + * @param string $db The database to read from + * @param string $table The table to read from + * @return void + */ +function getContent($db, $table) +{ + global $dbo, $config, $dump, $lang, $log; + + $content = ''; + $fields = $dbo->getTableColumns($table, $db); + // TODO decide if the type of field needs to be escaped and placed between quotes + // also handle NULL-values very strict for MySQL-servers running with sql-mod=STRICT + $fieldNames = array_keys($fields); + $fieldList = '`' . implode('`,`', $fieldNames) . '`'; + // indicator if the actual table is fully dumped in this call + $tableDone = 0; + $sql = 'SELECT * FROM `' . $db . '`.`' . $table . '` LIMIT ' + . $dump['table_record_offset'] . ',' . ($dump['restzeilen'] + 1); + $result = $dbo->query($sql, MsdDbFactory::ARRAY_NUMERIC); + $numRows = @count($result); + if ($numRows > 0) { + // we've got records - get fields + $numfields = count($result[0]); + if ($numRows > $dump['restzeilen']) { + // there are more records to get - table is not fully dumped + $dump['table_record_offset'] += $dump['restzeilen']; //set table record offset for next call + $numRows--; // correct counter - we only used the last record to find out if there is more to fetch + unset($result[$numRows]); + } else { + // table is done -> increase table offset + $recordsSaved = $dump['table_record_offset'] + $numRows; + $log->write( + Log::PHP, + sprintf( + $lang['L_BACKUP_TABLE_DONE'], + $table, + String::formatNumber($recordsSaved) + ) + ); + $dump['table_offset']++; + $dump['table_offset_total']++; + $dump['table_record_offset'] = 0; + $tableDone = 1; + } + foreach ($result as $row) { + //if($config['backup_using_updates']==1){ + $insert = 'INSERT INTO `' . $table . '` (' . $fieldList + . ') VALUES ('; + //TODO implement REPLACE INTO for expert mode + // } + //else{ + //$insert='REPLACE INTO `'.$table.'` '.$complete.' VALUES ('; + // } + + foreach ($row as $field => $val) { + if ($val != '') $insert .= '\'' . $dbo->escape($val) . '\','; + else $insert .= '\'\','; + } + $insert = substr($insert, 0, -1) . ');' . "\n"; + $dump['data'] .= $insert; + $dump['restzeilen']--; + $dump['countdata']++; + if (strlen($dump['data']) > $config['memory_limit'] + || ($config['multi_part'] == 1 + && strlen($dump['data']) + MULTIPART_FILESIZE_BUFFER + > $config['multipart_groesse'])) { + writeToDumpFile(); + } + } + if ($tableDone == 1) { + // check if records have been saved and add "enable keys" + $tables = $dump['databases'][$dump['db_actual']]['tables']; + if ($tables[$table]['dump_records'] == 1) { + $dump['data'] .= "/*!40000 ALTER TABLE `$table`" + ." ENABLE KEYS */;"; + } + } + } else { + // table corrupt -> skip it + $dump['table_offset']++; + $dump['table_offset_total']++; + $dump['table_record_offset'] = 0; + $dump['restzeilen'] = $dump['restzeilen'] - $numRows; + $dump['data'] .= "/*!40000 ALTER TABLE `$table` ENABLE KEYS */;\n"; + if (strlen($dump['data']) > $config['memory_limit'] + || ($config['multi_part'] == 1 && strlen($dump['data']) + + MULTIPART_FILESIZE_BUFFER > $config['multipart_groesse'])) { + writeToDumpFile(); + } + } +} + +/** + * Saves the created data of global var $dump['data'] to the dump file. + * + * If Multipart is used and the maximum filesize is reached a new file is + * created. Sets global var $dump['filesize'] to new vaule for printing + * on sccreen. + * + * @return void + */ +function writeToDumpFile() +{ + global $config, $dump; + $file = $config['paths']['backup'] . $dump['backupdatei']; + + if ($config['compression'] == 1) { + if ($dump['data'] != '') { + $fp = gzopen($file, 'ab'); + gzwrite($fp, $dump['data']); + gzclose($fp); + } + } else { + if ($dump['data'] != '') { + $fp = fopen($file, 'ab'); + fwrite($fp, $dump['data']); + fclose($fp); + } + } + $dump['data'] = ''; + clearstatcache(); + $dump['filesize'] = intval(@filesize($file)); + // if Multipart is used and maximum filesize is reached -> create new file + if ($config['multi_part'] == 1) { + if ($dump['filesize'] + MULTIPART_FILESIZE_BUFFER + > $config['multipart_groesse']) { + @chmod($file, 0777); + createNewFile(); + } + } +} + +/** + * Checks if there is a next db to be dumped + * + * Sets the global flag $dump['backup_done'] + * + * @return void + */ +function checkForNextDB() +{ + global $dump; + // a check, if another db should be saved is at the end of the script + // backup of actual db is done -> lets check if there is more to do + $nextDb = getNextKey($dump['databases'], $dump['db_actual']); + if ($nextDb !== false) { + $dump['backup_done'] = 0; + //-1 instead of 0 is needed for the execution of command before backup + $dump['table_offset'] = -1; + $dump['db_actual'] = $nextDb; + $dump['part_offset'] = $dump['part'] - 1; + } else { + $dump['backup_done'] = 1; + $dump['table_offset_total']--; + } +} + +/** + * Execute queries before and after the backup process + * + * Queries are saved in the configuration profile + * + * @param string $when Before (b) or after backup process + * + * @return void + */ +function executeCommand($when) +{ + // TODO implement execution of command before/after backup + return; +} + +/** + * Send e-mail and attach file + * + * @param string $file + * @return boolean + */ +function doEmail($file) +{ + global $config, $dump, $lang, $log; + include ('lib/phpmailer/php5/class.phpmailer.php'); + include ('inc/classes/helper/Html.php'); + // get some status info from actual file + $rootpath = $config['paths']['root'] . $config['paths']['backup']; + $fileInfo = ReadStatusline($file); + $fileInfo['size'] = @filesize($rootpath . $file); + $database = $fileInfo['dbname']; + $tablesSaved = $fileInfo['tables']; + $recordsSaved = $fileInfo['tables']; + + if (sizeof($_SESSION['email']['filelist']) == 0) { + // first call after backup -> create file list of all files for each database + $_SESSION['email']['filelist'] = array(); + foreach ($_SESSION['log']['email'] as $filename) { + $statusInfo = ReadStatusline($filename); + if (!isset($_SESSION['email']['filelist'][$statusInfo['dbname']])) { + $_SESSION['email']['filelist'][$statusInfo['dbname']] = array(); + } + $_SESSION['email']['filelist'][$statusInfo['dbname']][] = $filename; + } + } + // create file list for specific database + $filelist = ''; + foreach ($_SESSION['email']['filelist'][$database] as $filename) { + $phpSelf = $_SERVER['PHP_SELF']; + $linkToFile = '' + . $filename . ''; + $filelist .= $linkToFile; + if ($file == $filename && $config['email']['attach_backup']) { + $filelist .= ' (' . $lang['L_ATTACHED_AS_FILE'] . ')'; + } + $filelist .= '
' . "\n"; + } + + $mail = new PHPMailer(); + $mail->CharSet = 'utf-8'; + $mail->PlugInDir = 'lib/phpmailer/php5/'; + $mail->From = $config['email']['sender_address']; + $mail->FromName = $config['email']['sender_name']; + $mail->AddAddress($config['email']['recipient_address'], $config['email']['recipient_name']); + + // add cc-recipients + foreach ($config['email']['recipient_cc'] as $recipient) { + if ($recipient['address'] > '') { + $mail->AddCC($recipient['address'], $recipient['name']); + } + } + //build subject + $subject = $lang['L_DUMP_FILENAME'] . ': ' . $file; + if ($fileInfo['comment'] > '') { + $subject = $fileInfo['comment'] . ', ' . $subject; + } + $mail->Subject = $subject; + + $mail->Timeout = 60; + // set used mail-method + $mail->IsMail(); //defaults to php-mail-function + if ($config['use_mailer'] == 1) { + $mail->IsSendmail(); + $mail->Sendmail = $config['sendmail_call']; + } elseif ($config['use_mailer'] == 2) { + $mail->IsSMTP(); + //debug + //$mail->SMTPDebug = PHP_INT_MAX; + $mail->Host = $config['smtp_server']; + $mail->Port = $config['smtp_port']; + // auth? + if ($config['smtp_useauth']) { + $mail->SMTPAuth = true; + $mail->Username = $config['smtp_user']; + $mail->Password = $config['smtp_pass']; + } + //use ssl? + if ($config['smtp_usessl']) { + $mail->SMTPSecure = 'tls'; + } + } + + //build mail body + $body = ''; + + //add attachement? + if ($config['email']['attach_backup']) { + //check if file is bigger than allowed max size + if ($config['email_maxsize'] > 0 + && $fileInfo['size'] > $config['email_maxsize']) { + // attachement too big -> don't attach and paste message to body + $body .= sprintf( + $lang['L_EMAILBODY_TOOBIG'], + byteOutput($config['email_maxsize']), + $database, + $file . ' (' . byte_output( + filesize($config['paths']['backup'] . $file) + ) + . ')
' + ); + } else { + // add file as attachement + $mail->AddAttachment($rootpath . $file); + $body .= sprintf($lang['L_EMAILBODY_ATTACH'], $database, $filelist); + } + } else { + // don't attach backup file according to configuration + $body .= sprintf( + $lang['L_EMAILBODY_TOOBIG'], + byteOutput($config['email_maxsize']), + $database, + "$file (" . byteOutput( + filesize($config['paths']['backup'] . $file) + ) + . ")
" + ); + } + + //set body + $mail->MsgHTML($body); + //build alternative-body without tags for mail-clients blocking HTML + $altBody = strip_tags(Html::br2nl($body)); + $mail->AltBody = $altBody; + + $mail->Timeout = 30; + $ret = $mail->Send(); + if (!$ret) { + writeToErrorLog( + '', '', $lang['L_MAILERROR'] . ' -> ' . $mail->ErrorInfo, 0 + ); + $log->write(Log::PHP, $lang['L_MAILERROR']); + } else { + $msg = $lang['L_EMAIL_WAS_SEND'] . "`" + . $config['email']['recipient_address']; + $log->write(Log::PHP, $msg); + } + return $ret; +} + +/** + * Transfers a file via FTP and logs each action + * + * @param integer $ftpConnectionIndex Index of FTP-Connection in configuration + * @param string $sourceFile File to transfer + * @return void + */ +function sendViaFTP($ftpConnectionIndex, $sourceFile) +{ + global $config, $lang, $log; + + $upload = false; + $i = $ftpConnectionIndex; // I am lazy ;) + // connect to ftp server + if ($config['ftp'][$i]['ssl'] == 0) { + $connId = @ftp_connect( + $config['ftp'][$i]['server'], + $config['ftp'][$i]['port'], + $config['ftp'][$i]['timeout'] + ); + } else { + $connId = @ftp_ssl_connect( + $config['ftp'][$i]['server'], + $config['ftp'][$i]['port'], + $config['ftp'][$i]['timeout'] + ); + } + + if (is_resource($connId)) { + $log->write( + Log::PHP, + $lang['L_FTP'] . ': ' . + sprintf( + $lang['L_FTP_CONNECTION_SUCCESS'], + $config['ftp'][$i]['server'], + $config['ftp'][$i]['port'] + ) + ); + } else { + $msg = sprintf( + $lang['L_FTP_CONNECTION_ERROR'], + $config['ftp'][$i]['server'], + $config['ftp'][$i]['port'] + ); + writeToErrorLog('', '', $lang['L_FTP'] . ': ' . $msg, 0); + } + + // login using user and password + $loginResult = @ftp_login( + $connId, + $config['ftp'][$i]['user'], + $config['ftp'][$i]['pass'] + ); + if (!$loginResult) { + writeToErrorLog( + '', + '', + $lang['L_FTP'] . ': ' . sprintf( + $lang['L_FTP_LOGIN_ERROR'], + $config['ftp'][$i]['user'] + ), + 0 + ); + } else { + $log->write( + Log::PHP, + $lang['L_FTP'] . ': ' . sprintf( + $lang['L_FTP_LOGIN_SUCCESS'], + $config['ftp'][$i]['user'] + ) + ); + } + if ($config['ftp'][$i]['mode'] == 1) { + if (@ftp_pasv($connId, true)) { + $log->write( + Log::PHP, + $lang['L_FTP'] . ': ' . $lang['L_FTP_PASV_SUCCESS'] + ); + } else { + writeToErrorLog( + '', '', + $lang['L_FTP'] . ': ' . $lang['L_FTP_PASV_ERROR'], 0 + ); + } + } + + // Upload der Datei + $dest = $config['ftp'][$i]['dir'] . $sourceFile; + $source = $config['paths']['backup'] . $sourceFile; + $upload = @ftp_put($connId, $dest, $source, FTP_BINARY); + // Upload-Status überprüfen + if (!$upload) { + writeToErrorLog( + '', '', sprintf($lang['L_FTP_FILE_TRANSFER_ERROR'], $sourceFile), 0 + ); + } else { + $log->write( + Log::PHP, + sprintf($lang['L_FTP_FILE_TRANSFER_SUCCESS'], $sourceFile) + ); + } + + // Schließen des FTP-Streams + @ftp_quit($connId); + $log->write(Log::PHP, $lang['L_FTP_CONNECTION_CLOSED']); +} + +/** + * Gets all information about a dump process and stores it in global $dump-Array + * + * @return void + */ +function prepareDumpProcess() +{ + global $databases, $dump, $config, $tableInfos; + $dump['databases'] = array(); + $dump['records_total'] = 0; + $dump['tables_total'] = 0; + $dump['datasize_total'] = 0; + // make copy of database-array to make changes for value "dump" just here + $dbs = $databases; + // first check if any db is marked to be dumped + $dbToDumpExists = false; + foreach ($dbs as $val) { + if (isset($val['dump']) && $val['dump'] == 1) { + // Db should be saved + $dbToDumpExists = true; + break; + } + } + // no db selected for dump -> set actual db to be dumped + if (!$dbToDumpExists) { + $dbs[$config['db_actual']]['dump'] = 1; + } + + // find out which databases and tables should be saved + // dump=0 -> don't dump records + // dump=1 -> dump records using "INSERT INTO" + foreach ($dbs as $dbName => $val) { + if (isset($val['dump']) && $val['dump'] > 0) { + // db should be dumped + // now lets check which tables should be saved + // for now we save all tables -> later check prefixes etc... + $tableInfos = getTableInfo($dbName); + if (isset($tableInfos[$dbName])) { + if (!isset($dump['databases'][$dbName])) { + $dump['databases'][$dbName] = array(); + } + // calculate sums + $dump['databases'][$dbName] = $tableInfos[$dbName]; + $dump['databases'][$dbName]['prefix'] = ''; + if (isset($databases[$dbName]['prefix'])) { + $dump['databases'][$dbName]['prefix'] = + $databases[$dbName]['prefix']; + } + $dump['records_total'] += + $dump['databases'][$dbName]['records_total']; + $dump['tables_total'] += + $dump['databases'][$dbName]['table_count']; + $dump['datasize_total'] += + $dump['databases'][$dbName]['datasize_total']; + + // check if tables are selected -> + // then remove all others from array and correct sums + if ($dbName == $_SESSION['config']['db_actual'] + && isset($dump['selected_tables']) + && is_array($dump['selected_tables']) + && count($dump['selected_tables']) > 0) { + foreach ($dump['databases'][$dbName]['tables'] + as $tablename => $val) { + if (!in_array($tablename, $dump['selected_tables'])) { + $dump['databases'][$dbName]['tables'][$tablename]['dump_structure'] = 0; + $dump['databases'][$dbName]['tables'][$tablename]['dump_records'] = 0; + + // remove table from todo-list + unset($dump['databases'][$dbName]['tables'][$tablename]); + // substract values of table from sums + $dump['tables_total']--; + $dump['databases'][$dbName]['table_count']--; + $dump['databases'][$dbName]['records_total'] -= $val['records']; + $dump['databases'][$dbName]['datasize_total'] -= $val['data_length']; + $dump['databases'][$dbName]['size_total'] -= $val['data_length'] + $val['index_length']; + $dump['datasize_total'] -= $val['data_length']; + $dump['records_total'] -= $val['records']; + } + } + } + } + } + } + // set db to be dumped first -> start index is needed + $dbNames=array_keys($dump['databases']); + $dump['db_actual'] = $dbNames[0]; +} + diff --git a/inc/functions/functions_files.php b/inc/functions/functions_files.php new file mode 100644 index 0000000..c69caa6 --- /dev/null +++ b/inc/functions/functions_files.php @@ -0,0 +1,370 @@ +getFilename(); + if ($file[0] != '.' && $fileinfo->isFile()) + { + $size = byteOutput($fileinfo->getSize()); + $files["$file"] = $file . ' (' . $size . ')'; + } + } + ksort($files); + $r = Html::getOptionlist($files, $selected); + return $r; +} + +/** + * Reformat a date from format dd.mm.yyyy hh:mm to yyyy.mm.dd hh:mm to make it sortable. + * + * @param string $datum Datetime taken from filename + * @return string + */ +function getSortableDate($datum) +{ + $p = explode(' ', $datum); + $uhrzeit = $p[1]; + $p2 = explode('.', $p[0]); + $day = $p2[0]; + $month = $p2[1]; + $year = $p2[2]; + return $year . '.' . $month . '.' . $day . ' ' . $uhrzeit; +} + +/** + * Checks a dump file and converts it. + * + * Places field names in backticks and splitts files into 10 MB-Parts. + * + * @param string $filesource File to convert + * @param string $db_name Database name will be used to create filenames and statusline + * @param string $cp Target copy file + * @return void + */ +function convert($filesource, $db_name, $cp) +{ + global $config, $lang; + @ini_set('max_input_time', '0'); // for real big dumps + + + $filesize = 0; + $max_filesize = 1024 * 1024 * 10; //10 MB splitsize + $part = 1; + $cps = (substr(strtolower($filesource), -2) == "gz") ? 1 : 0; + // we compare the string size with this value (not the real filesie) + //so if file is compressed we need to adjust it + if ($cps == 1) $max_filesize *= 7; + + $filedestination = $db_name . '_' . date("Y_m_d_H_i", time()); + echo "
" . sprintf($lang['L_CONVERT_FILEREAD'], $filesource) . ".....
"; + if (file_exists($config['paths']['backup'] . $filedestination)) unlink($config['paths']['backup'] . $filedestination); + $f = ($cps == 1) ? gzopen($config['paths']['backup'] . $filesource, "r") : fopen($config['paths']['backup'] . $filesource, "r"); + $z = ($cp == 1) ? gzopen($config['paths']['backup'] . $filedestination . '_part_1.sql.gz', "w") : fopen($config['paths']['backup'] . $filedestination . '_part_1.sql', "w"); + + $zeile = getPseudoStatusline($part, $db_name) . "\r\n"; + ($cp == 1) ? gzwrite($z, $zeile) : fwrite($z, $zeile); + $zeile = ''; + flush(); + + $insert = $mode = ""; + $n = 0; + $eof = ($cps == 1) ? gzeof($f) : feof($f); + $splitable = false; // can the file be splitted? Try to avoid splitting before a command is completed + WHILE (!$eof) + { + flush(); + $eof = ($cps == 1) ? gzeof($f) : feof($f); + $zeile = ($cps == 1) ? gzgets($f, 5144000) : fgets($f, 5144000); + + $t = strtolower(substr($zeile, 0, 10)); + if ($t > '') + { + switch ($t) + { + case 'insert int': + { + // eine neue Insert Anweisung beginnt + if (strpos($zeile, '(') === false) + { + //Feldnamen stehen in der naechsten Zeile - holen + $zeile .= "\n\r"; + $zeile .= ($cps == 1) ? trim(gzgets($f, 8192)) : trim(fgets($f, 8192)); + $zeile .= ' '; + } + + // get INSERT-Satement + $insert = substr($zeile, 0, strpos($zeile, '(')); + if (substr(strtoupper($insert), -7) != 'VALUES ') $insert .= ' VALUES '; + $mode = 'insert'; + $splitable = false; + break; + } + + case 'create tab': + { + $mode = 'create'; + WHILE (substr(rtrim($zeile), -1) != ';') + { + $zeile .= fgets($f, 8192); + } + $zeile = setBackticks($zeile) . "\n\r"; + $splitable = true; + break; + } + } + } + + if ($mode == 'insert') + { + if (substr(rtrim($zeile), strlen($zeile) - 3, 2) == ');') $splitable = true; + + // Komma loeschen + $zeile = str_replace('),(', ");\n\r" . $insert . ' (', $zeile); + } + + if ($splitable == true && $filesize > $max_filesize) // start new file? + { + $part++; + if ($mode == 'insert') // Insert -> first complete Insert-Statement, then begin new file + { + if ($cp == 1) + { + gzwrite($z, $zeile); + gzclose($z); + $z = gzopen($config['paths']['backup'] . $filedestination . '_part_' . $part . '.sql.gz', "w"); + $zeile = getPseudoStatusline($part, $db_name) . "\r\n"; + gzwrite($z, $zeile); + $zeile = ''; + } + else + { + fwrite($z, $zeile); + fclose($z); + $z = fopen($config['paths']['backup'] . $filedestination . '_part_' . $part . '.sql', "w"); + $zeile = getPseudoStatusline($part, $db_name) . "\r\n"; + gzwrite($z, $zeile); + $zeile = ''; + } + } + else // first close last file, then begin new one and write new beginning command + { + if ($cp == 1) + { + gzclose($z); + $z = gzopen($config['paths']['backup'] . $filedestination . '_part_' . $part . '.sql.gz', "w"); + $zeile = getPseudoStatusline($part, $filedestination) . "\r\n" . $zeile; + gzwrite($z, $zeile); + } + else + { + fclose($z); + $z = fopen($config['paths']['backup'] . $filedestination . '_part_' . $part . '.sql', "w"); + $zeile = getPseudoStatusline($part, $filedestination) . "\r\n" . $zeile; + fwrite($z, $zeile); + } + $n = 0; + } + $filesize = 0; + $splitable = false; + } + else // no, append to actual file + { + $filesize += strlen($zeile); + if ($n > 200) + { + $n = 0; + echo '
'; + } + echo '.'; + if ($cps == 1) gzwrite($z, $zeile); + else fwrite($z, $zeile); + flush(); + } + $n++; + } + $zeile = "\n-- EOB"; + if ($cps == 1) + { + gzwrite($z, $zeile); + gzclose($z); + } + else + { + fwrite($z, $zeile); + fclose($z); + } + + if ($cps == 1) gzclose($f); + else fclose($f); + echo '
' . sprintf($lang['L_CONVERT_FINISHED'], $filedestination) . '
'; +} + +/** + * Create a dummy statusline. + * + * Used when converting a file, to create a legal MSD-Multipart-File. + * + * @param integer $part + * @param string $db_name + * @return string + */ +function getPseudoStatusline($part, $db_name) +{ + if ($part > 1) echo '
Continue with part: ' . $part . '
'; + $ret = '-- Status:-1:-1:MP_' . ($part) . ':' . $db_name . ":php:converter2:converted:unknown:1:::latin1:EXTINFO\r\n" . "-- TABLE-INFO\r\n" . "-- TABLE|unknown|0|0|2009-01-24 20:39:39\r\n" . "-- EOF TABLE-INFO\r\n"; + return $ret; +} + +/** + * Read information from all backup files in folder work/backup and return multidimensional array + * containing all info. + * + * @return array + */ +function getBackupfileInfo() +{ + global $config; + clearstatcache(); + $files = Array(); + $dh = opendir($config['paths']['backup']); + while (false !== ($filename = readdir($dh))) + { + if ($filename != '.' && $filename != '..' && !is_dir($config['paths']['backup'] . $filename)) + { + $files[]['name'] = $filename; + } + } + $arrayindex = 0; + $total_filesize = 0; + $db_backups = array(); + $db_summary_anzahl = array(); + $db_summary_last = array(); + if (count($files) > 0) + { + for ($i = 0; $i < sizeof($files); $i++) + { + // filesize + $size = filesize($config['paths']['backup'] . $files[$i]['name']); + $file_datum = date("d\.m\.Y H:i", filemtime($config['paths']['backup'] . $files[$i]['name'])); + $statusline = ReadStatusline($files[$i]['name']); + $backup_timestamp = GetTimestampFromFilename($files[$i]['name']); + $pathinfo = pathinfo($files[$i]['name']); + $file_extension = $pathinfo['extension']; + if ($backup_timestamp == '') $backup_timestamp = $file_datum; + $database_name = $statusline['dbname']; + + // check for some special cases + if ($database_name == 'unknown') $database_name = '~unknown'; // needed for sorting - place unknown files at the end + if ($statusline['comment'] == 'converted') $database_name = '~converted'; // converted fiels + + + //jetzt alle in ein Array packen + if ($statusline['part'] == 'MP_0' || $statusline['part'] == '') + { + $db_backups[$arrayindex]['name'] = $files[$i]['name']; + $db_backups[$arrayindex]['db'] = $database_name; + $db_backups[$arrayindex]['extension'] = $file_extension; + $db_backups[$arrayindex]['size'] = $size; + $db_backups[$arrayindex]['date'] = $backup_timestamp; + $db_backups[$arrayindex]['sort'] = getSortableDate($backup_timestamp); + $db_backups[$arrayindex]['tables'] = $statusline['tables']; + $db_backups[$arrayindex]['records'] = $statusline['records']; + $db_backups[$arrayindex]['multipart'] = 0; + $db_backups[$arrayindex]['comment'] = $statusline['comment']; + $db_backups[$arrayindex]['script'] = ($statusline['script'] != '') ? $statusline['script'] . '(' . $statusline['scriptversion'] . ')' : ''; + $db_backups[$arrayindex]['charset'] = $statusline['charset']; + $db_backups[$arrayindex]['mysqlversion'] = $statusline['mysqlversion']; + if (!isset($db_summary_last[$database_name])) $db_summary_last[$database_name] = $backup_timestamp; + $db_summary_anzahl[$database_name] = (isset($db_summary_anzahl[$database_name])) ? $db_summary_anzahl[$database_name] + 1 : 1; + $db_summary_size[$database_name] = (isset($db_summary_size[$database_name])) ? $db_summary_size[$database_name] + $size : $size; + if (getSortableDate($backup_timestamp) > getSortableDate($db_summary_last[$database_name])) $db_summary_last[$database_name] = $backup_timestamp; + } + else + { + //v($statusline); + //list multipart files only once but keep info how many files belong to this backup + $done = 0; + if (!isset($db_summary_size[$database_name])) { + $db_summary_size[$database_name] = 0; + } + for ($j = 0; $j < $arrayindex; $j++) + { + if (isset($db_backups[$j])) + { + if (($db_backups[$j]['date'] == $backup_timestamp) && $db_backups[$j]['db'] == $database_name && $db_backups[$j]['extension'] == $file_extension) + { + $db_backups[$j]['mysqlversion'] = $statusline['mysqlversion']; + $db_backups[$j]['multipart']++; + $db_backups[$j]['size'] += $size; // calculate size for this multipart backup + $db_summary_size[$database_name] += $size; // calculate total size for this database + $done = 1; + break; + } + } + } + if ($done == 1) $arrayindex--; + + if ($done == 0) + { + //new entry for this backup with this timestamp + $db_backups[$arrayindex]['name'] = $files[$i]['name']; + $db_backups[$arrayindex]['db'] = $database_name; + $db_backups[$arrayindex]['extension'] = $file_extension; + $db_backups[$arrayindex]['size'] = $size; + $db_backups[$arrayindex]['date'] = $backup_timestamp; + $db_backups[$arrayindex]['sort'] = getSortableDate($backup_timestamp); + $db_backups[$arrayindex]['tables'] = $statusline['tables']; + $db_backups[$arrayindex]['records'] = $statusline['records']; + $db_backups[$arrayindex]['multipart'] = 1; + $db_backups[$arrayindex]['comment'] = $statusline['comment']; + $db_backups[$arrayindex]['script'] = ($statusline['script'] != "") ? $statusline['script'] . "(" . $statusline['scriptversion'] . ")" : ""; + $db_backups[$arrayindex]['charset'] = $statusline['charset']; + + if (!isset($db_summary_last[$database_name])) $db_summary_last[$database_name] = $backup_timestamp; + $db_summary_anzahl[$database_name] = (isset($db_summary_anzahl[$database_name])) ? $db_summary_anzahl[$database_name] + 1 : 1; + $db_summary_size[$database_name] = (isset($db_summary_size[$database_name])) ? $db_summary_size[$database_name] + $size : $size; + if (getSortableDate($backup_timestamp) > getSortableDate($db_summary_last[$database_name])) $db_summary_last[$database_name] = $backup_timestamp; + } + } + $arrayindex++; + $total_filesize += $size; // calculate overall file size + } + } + if ((isset($db_backups)) && (is_array($db_backups))) $db_backups = arfsort($db_backups, get_orderarray('sort,d|name,A')); + // merge infos into one array + $info = array(); + $info['filesize_total'] = $total_filesize; // total size of all files + $info['files'] = $db_backups; // info per file + unset($db_backups); + $info['databases'] = array(); + foreach ($db_summary_anzahl as $db => $count) + { + $info['databases'][$db]['backup_count'] = $count; + $info['databases'][$db]['backup_size_total'] = $db_summary_size[$db]; + $info['databases'][$db]['latest_backup_timestamp'] = $db_summary_last[$db]; + } + return $info; +} diff --git a/inc/functions/functions_global.php b/inc/functions/functions_global.php new file mode 100644 index 0000000..4f36d22 --- /dev/null +++ b/inc/functions/functions_global.php @@ -0,0 +1,915 @@ +'; + if (is_array($t) || is_object($t)){ + echo '
';
+        print_r($t);
+        echo '
'; + }else + echo $t; +} + } +/** + * Detect server protocol + * + * @return string 'https://' || 'http://' + */ +function getServerProtocol() +{ + return (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? 'https://' : 'http://'; +} +/** + * Build order array from string 'col1,A|col2,d' for function arfosort + * + * @param string $order Orderstring + * + * @return array + */ +function get_orderarray($order) +{ + $order_arr = array(); + $orders = explode('|', $order); + foreach ($orders as $o){ + $d = explode(',', $o); + if (isset($d[0]) && isset($d[1])) + $order_arr[] = array($d[0], $d[1]); + } + return $order_arr; +} +/** + * Order multidimensial array + * + * @param array $a Array to sort + * @param string $fl Order-string to define what keys should be sortet in what direction + * + * @return array Sorted array + */ +function arfsort($a, $fl) +{ + $GLOBALS['__ARFSORT_LIST__'] = $fl; + usort($a, 'arfsort_func'); + return $a; +} +/** + * Sort a multidimenional array no matter in which depth the key is + * + * @param array $a Array to sort + * @param string $b Sortstring containing keys and kind of sorting + * + * @return mixed + */ +function arfsort_func($a, $b) +{ + foreach ($GLOBALS['__ARFSORT_LIST__'] as $f){ + if (isset($b[$f[0]]) && isset($a[$f[0]])){ + switch ($f[1]){ // switch on ascending or descending value + case 'd': + $strc = strcmp(strtolower($b[$f[0]]), strtolower($a[$f[0]])); + if ($strc != 0) + return $strc; + break; + case 'a': + $strc = strcmp(strtolower($a[$f[0]]), strtolower($b[$f[0]])); + if ($strc != 0) + return $strc; + break; + case 'D': + $strc = (floatval($b[$f[0]]) < floatval($a[$f[0]])) ? -1 : 1; + if ($b[$f[0]] != $a[$f[0]]) + return $strc; + break; + case 'A': + $strc = (floatval($b[$f[0]]) > floatval($a[$f[0]])) ? -1 : 1; + if ($b[$f[0]] != $a[$f[0]]) + return $strc; + break; + } + } + } + return 0; +} +/** + * Read table information about nr of records and more for given database name. + * Return array with advsanced information. + * + * @param string $db The database name to read info from + * @param string $table If set, only information for this table is filled + * + * @return array + */ +function getTableInfo($db, $table = '') +{ + global $dbo, $config; + $tableInfos=array(); + $res = $dbo->selectDb($db); + if ($res){ + $query = 'SHOW TABLE STATUS FROM `' . $db . '`'; + if ($table > '') { + $query .= ' LIKE \'' . $table . '\''; + } + $res = $dbo->query($query, MsdDbFactory::ARRAY_ASSOC); + // init index if not set + if (!isset($tableInfos[$db])) + $tableInfos[$db] = array(); + if (!isset($tableInfos[$db]['tables'])) + $tableInfos[$db]['tables'] = array(); + $tableInfos[$db]['table_count'] = sizeof($res); + $tableInfos[$db]['records_total'] = 0; + $tableInfos[$db]['datasize_total'] = 0; + $tableInfos[$db]['size_total'] = 0; + if ($tableInfos[$db]['table_count'] > 0){ + for ($i = 0, $max = $tableInfos[$db]['table_count']; $i < $max; $i++){ + $row = $res[$i]; + $n = $row['Name']; + if (!isset($tableInfos[$db]['tables'][$n])) + $tableInfos[$db]['tables'][$row['Name']] = array(); + if (isset($row['Type'])) + $row['Engine'] = $row['Type']; + $tableInfos[$db]['tables'][$n]['name'] = $row['Name']; + $tableInfos[$db]['tables'][$n]['engine'] = $row['Engine']; + $tableInfos[$db]['tables'][$n]['dump_structure'] = 1; + $tableInfos[$db]['tables'][$n]['dump_records'] = 1; + // if we have a VIEW or a table of Type MEMORY -> don't save records + if (strtoupper($row['Comment']) == 'VIEW' || (isset($row['Engine']) && in_array(strtoupper($row['Engine']), array( + 'MEMORY')))){ + $tableInfos[$db]['tables'][$n]['dump_records'] = 0; + } + if (!isset($row['Update_time'])) + $row['Update_time'] = ''; + $tableInfos[$db]['tables'][$n]['update_time'] = $row['Update_time']; + $tableInfos[$db]['tables'][$n]['data_free'] = isset($row['Data_free']) ? $row['Data_free'] : 0; + $tableInfos[$db]['tables'][$n]['collation'] = isset($row['Collation']) ? $row['Collation'] : ''; + $tableInfos[$db]['tables'][$n]['comment'] = isset($row['Comment']) ? $row['Comment'] : ''; + $tableInfos[$db]['tables'][$n]['auto_increment'] = isset($row['Auto_increment']) ? $row['Auto_increment'] : ''; + $tableInfos[$db]['tables'][$n]['records'] = (int) $row['Rows']; + $tableInfos[$db]['tables'][$n]['data_length'] = (float) $row['Data_length']; + $tableInfos[$db]['tables'][$n]['index_length'] = $row['Index_length']; + $tableInfos[$db]['records_total'] += (int) $row['Rows']; + $tableInfos[$db]['datasize_total'] += (float) $row['Data_length']; + $tableInfos[$db]['size_total'] += (float) $row['Data_length'] + (float) $row['Index_length']; + } + } + } + return $tableInfos; +} +/** + * Returns microtime of now as float value + * + * @return float microtime + */ +function getMicrotime() +{ + list ($usec, $sec) = explode(' ', microtime()); + return ((float) $usec + (float) $sec); +} +/** + * Detect free diskspace + * + * @return string Space in human readable Bytes or message if not available + */ +function getFreeDiskSpace() +{ + global $lang; + $dfs = @diskfreespace("../"); + return ($dfs) ? byteOutput($dfs) : $lang['L_NOTAVAIL']; +} +/** + * Extract timestamp informations from a filename and return formatted string YYYY.MM.DD HH:MM + * + * @param string $s Filename as input 'dbname_2009_10_18_16_22_part1_sql-gz' + * + * @return string Formated timestamp YYYY.MM.DD HH:MM + */ +function getTimestampFromFilename($s) +{ + $i = strpos(strtolower($s), 'part'); + if ($i > 0) + $s = substr($s, 0, $i - 1); + $i = strpos(strtolower($s), 'crondump'); + if ($i > 0) + $s = substr($s, 0, $i - 1); + $i = strpos(strtolower($s), '.sql'); + if ($i > 0) + $s = substr($s, 0, $i); + $sp = explode('_', $s); + $anz = count($sp) - 1; + if (strtolower($sp[$anz]) == 'perl') + $anz--; + if ($anz > 4){ + return $sp[$anz - 2] . '.' . $sp[$anz - 3] . '.' . $sp[$anz - 4] . ' ' . $sp[$anz - 1] . ':' . $sp[$anz]; + }else{ + //no MySQLDumper file + return ''; + } +} +/** + * Writes errors or notices to the corresponding error log + * + * @param string $db Affected database + * @param string $sql The executed query + * @param string $error The error message to log + * @param int $art The kind of error (0=error, 1=notice) + * + * @return void + */ +function writeToErrorLog($db = '', $sql = '', $error = '', $art = 1) +{ + global $config, $lang, $log; + $sql = str_replace("\r", '', $sql); + $sql = str_replace("\n\n", '
', $sql); + $sql = str_replace("\n", '
', $sql); + $sql = trim($sql); + $error = str_replace("\r", '', $error); + if ($art == 0) { + $errormsg = $lang['L_ERROR'] . ': ' . $error; + } else { + $errormsg = $lang['L_NOTICE'] . ': ' . $error; + } + // append query if set + if ($sql > '') { + $errormsg .= '
SQL: ' . $sql; + } + $time = date('d.m.Y H:i:s') . ' '; + if ($art == 0) { + $_SESSION['log']['errors'][] = $time.$errormsg; + } else { + $_SESSION['log']['notices'][] = $time.$errormsg; + } + $log->write(Log::ERROR, $errormsg); +} +/** + * Checks if the directories work, config, backup and log are writable + * Returns concatenated string with warnings or empty string if every directory is writable + * + * @return string + */ +function checkDirectories() +{ + global $config, $lang; + $warn = ''; + if (!is_writable($config['paths']['work'])) + $warn .= sprintf($lang['L_WRONG_RIGHTS'], $config['paths']['work'], '0777'); + if (!is_writable($config['paths']['config'])) + $warn .= sprintf($lang['L_WRONG_RIGHTS'], $config['paths']['config'], '0777'); + if (!is_writable($config['paths']['backup'])) + $warn .= sprintf($lang['L_WRONG_RIGHTS'], $config['paths']['backup'], '0777'); + if (!is_writable($config['paths']['log'])) + $warn .= sprintf($lang['L_WRONG_RIGHTS'], $config['paths']['log'], '0777'); + if ($warn != '') + $warn = '' . $warn . ''; + return $warn; +} +/** + * Deletes every table or view in a database + * + * @param string $dbn Databasename + * + * @return void + */ +function truncateDb($dbn) +{ + global $dbo; + $t_sql = array(); + $dbo->query('SET FOREIGN_KEY_CHECKS=0', MsdDbFactory::SIMPLE); + $res = $dbo->query('SHOW TABLE STATUS FROM `' . $dbn . '`', MsdDbFactory::ARRAY_ASSOC); + foreach ($res as $row){ + if (substr(strtoupper($row['Comment']), 0, 4) == 'VIEW'){ + $t_sql[] = 'DROP VIEW `' . $dbn . '``' . $row['Name'] . '`'; + }else{ + $t_sql[] = 'DROP TABLE `' . $dbn . '`.`' . $row['Name'] . '`'; + } + } + if (sizeof($t_sql) > 0){ + for ($i = 0; $i < count($t_sql); $i++){ + try{ + $dbo->query($t_sql[$i]); + }catch (Excption $e){ + //TODO create clean error handling depending on context + writeToErrorLog($e->getMessage()); + die($e->getMessage()); + } + } + } + $dbo->query('SET FOREIGN_KEY_CHECKS=1', MsdDbFactory::SIMPLE); +} +/** + * Delete old backups from folder work/backup according to configuration + * + * @return string Outputstring with messages about deleted files + */ +function doAutoDelete() +{ + global $config, $lang, $out; + $out = ''; + if ($config['auto_delete']['max_backup_files'] > 0){ + //Files einlesen + $dh = opendir($config['paths']['backup']); + $files = array(); + // Build assoc Array $db=>$timestamp=>$filenames + if (!function_exists('ReadStatusline')) + include ('./inc/functions/functions_files.php'); + while (false !== ($filename = readdir($dh))){ + if ($filename != '.' && $filename != '..' && !is_dir($config['paths']['backup'] . $filename)){ + $statusline = readStatusline($filename); + if ($statusline['dbname'] != 'unknown'){ + $dbName = $statusline['dbname']; + $datum = substr($filename, strlen($dbName) + 1); + $timestamp = substr($datum, 0, 16); + if (!isset($files[$dbName])) + $files[$dbName] = array(); + if (!isset($files[$dbName][$timestamp])) + $files[$dbName][$timestamp] = array(); + $files[$dbName][$timestamp][] = $filename; + } + } + } + $out = ''; // stores output messages + // Backups per DB and Timestamp + foreach ($files as $db => $val){ + if (sizeof($val) > $config['auto_delete']['max_backup_files']){ + $db_files = $val; + krsort($db_files, SORT_STRING); + //now latest backupfiles are on top -> delete all files with greater index + $i = 0; + foreach ($db_files as $timestamp => $filenames){ + if ($i >= $config['auto_delete']['max_backup_files']){ + // Backup too old -> delete files + foreach ($filenames as $f){ + if ($out == '') + $out .= $lang['L_FM_AUTODEL1'] . '
'; + if (@unlink('./' . $config['paths']['backup'] . $f)){ + $out .= '' . sprintf($lang['L_DELETE_FILE_SUCCESS'], $f) . '
'; + }else{ + $out .= $lang['L_ERROR'] . ':

' . sprintf($lang['L_DELETE_FILE_ERROR'], $f) . '


'; + } + } + } + $i++; + } + } + } + } + return $out; +} +/** + * Analyzes the first line of a MSD-Backup. Expects it as string parameter. + * + * @param string $line The statusline from the backup to analyze + * + * @return array Extracted information as array + */ +function readStatusline($filename) +{ + global $config; + /*AUFBAU der Statuszeile: + -- Status:nr of tables:records:Multipart:Databasename:script:scriptversion:Comment: + MySQL-Version:flags (unused):SQLCommandBeforeBackup:SQLCommandAfterBackup:Charset:EXTINFO + */ + $gz = substr($filename, -3) == '.gz' ? true : false; + $fh = $gz ? gzopen($config['paths']['backup'] . $filename, 'r') : fopen($config['paths']['backup'] . $filename, 'r'); + if (!$fh){ + v(debug_backtrace()); + die(); + } + $line = $gz ? gzgets($fh) : fgets($fh); + $gz ? gzclose($fh) : fclose($fh); + $statusline = array(); + $line = removeBom($line); + if ((substr($line, 0, 8) != "# Status" && substr($line, 0, 9) != "-- Status") || substr($line, 0, 10) == '-- StatusC'){ + //Fremdfile + $statusline['tables'] = -1; + $statusline['records'] = -1; + $statusline['part'] = 'MP_0'; + $statusline['dbname'] = 'unknown'; + $statusline['script'] = ''; + $statusline['scriptversion'] = ''; + $statusline['comment'] = ''; + $statusline['mysqlversion'] = 'unknown'; + $statusline['flags'] = '2222222'; + $statusline['sqlbefore'] = ''; + $statusline['sqlafter'] = ''; + $statusline['charset'] = '?'; + }else{ + // MySQLDumper-File - Informationen extrahieren + $s = explode(':', $line); + if (count($s) < 12){ + //fehlenden Elemente auffüllen + $c = count($s); + array_pop($s); + for ($i = $c - 1; $i < 12; $i++){ + $s[] = ''; + } + } + $statusline['tables'] = $s[1]; + $statusline['records'] = $s[2]; + $statusline['part'] = ($s[3] == '' || $s[3] == 'MP_0') ? 'MP_0' : $s[3]; + $statusline['dbname'] = $s[4]; + $statusline['script'] = $s[5]; + $statusline['scriptversion'] = $s[6]; + $statusline['comment'] = $s[7]; + $statusline['mysqlversion'] = $s[8]; + if ((isset($s[12])) && trim($s[12]) != 'EXTINFO'){ + $statusline['charset'] = $s[12]; + }else{ + $statusline['charset'] = '?'; + } + } + return $statusline; +} +/** + * Reads Head-Information about tables from MSD-Backup and returns it as array + * + * @param string $filename Filename of MSD-Backup to analyze + * + * @return array Detailed information about tables n MSD-Backup + */ +function getTableHeaderInfoFromBackup($filename) +{ + global $config; + // Get Tableinfo from file header + $tabledata = array(); + $i = 0; + $gz = substr($filename, -3) == '.gz' ? true : false; + $fh = $gz ? gzopen($config['paths']['backup'] . $filename, 'r') : fopen($config['paths']['backup'] . $filename, 'r'); + $eof = false; + WHILE (!$eof){ + $line = $gz ? gzgets($fh, 40960) : fgets($fh, 40960); + $line = trim($line); + if (substr($line, 0, 9) == '-- TABLE|'){ + $d = explode('|', $line); + $tabledata[$i]['name'] = $d[1]; + $tabledata[$i]['records'] = $d[2]; + $tabledata[$i]['size'] = $d[3]; + $tabledata[$i]['update'] = $d[4]; + $tabledata[$i]['engine'] = isset($d[5]) ? $d[5] : ''; + $i++; + }elseif (substr($line, 0, 6) == '-- EOF') + $eof = true; // End of Table-Info - >done + elseif (substr(strtolower($line), 0, 6) == 'create') + $eof = true; // we have found the first CREATE-Query -> done + } + $gz ? gzclose($fh) : fclose($fh); + return $tabledata; +} +/** + * Calculate next Multipart-Filename + * + * @param string $filename The filename to calculate the next name from + * + * @return string Filename of next Multipart-File + */ +function getNextPart($filename) +{ + $nf = explode('_', $filename); + $i = array_search('part', $nf) + 1; + $part = substr($nf[$i], 0, strpos($nf[$i], '.')); + $ext = substr($nf[$i], strlen($part)); + $nf[$i] = ++$part . $ext; + $filename = implode('_', $nf); + return $filename; +} +/** + * Formats seconds to human readable output + * + * @param integer $time Time in seconds + * + * @return string Time as human readable formatted string + */ +function getTimeFormat($time) +{ + global $lang; + $d = floor($time / 86400); + $h = floor(($time - $d * 86400) / 3600); + $m = floor(($time - $d * 86400 - $h * 3600) / 60); + $s = $time - $d * 86400 - $h * 3600 - $m * 60; + $ret = sprintf('%02d', $s) . ' ' . ($s == 1 ? $lang['L_SECOND'] : $lang['L_SECONDS']); + if ($m > 0){ + $ret = $m . ' ' . ($m == 1 ? $lang['L_MINUTE'] : $lang['L_MINUTES']) . ' ' . $ret; + } + if ($h > 0){ + $ret = $h . ' ' . ($h == 1 ? $lang['L_HOUR'] : $lang['L_HOURS']) . ' ' . $ret; + } + if ($d > 0){ + $ret = $d . ' ' . ($d == 1 ? $lang['L_DAY'] : $lang['L_DAYS']) . ' ' . $ret; + } + return $ret; +} +/** + * Tests a ftp-connection and returns messages about uccess or failure + * + * @param integer $i The index of the connection profile to test + * + * @return array Array with messages + */ +function testFTP($i) +{ + global $lang, $config; + if (!isset($config['ftp'][$i]['timeout'])) + $config['ftp'][$i]['timeout'][$i] = 30; + $ret = array(); + if ($config['ftp'][$i]['port'] == '' || $config['ftp'][$i]['port'][$i] == 0) + $config['ftp'][$i]['port'] = 21; + $pass = -1; + if (!extension_loaded("ftp")){ + $ret[] = '' . $lang['L_NOFTPPOSSIBLE'] . ''; + }else + $pass = 0; + if ($pass == 0){ + if ($config['ftp'][$i]['server'] == '' || $config['ftp'][$i]['user'] == ''){ + $ret[] = '' . $lang['L_WRONGCONNECTIONPARS'] . ''; + }else + $pass = 1; + } + if ($pass == 1){ + if ($config['ftp'][$i]['ssl'] == 0) + $conn_id = @ftp_connect($config['ftp'][$i]['server'], $config['ftp'][$i]['port'], $config['ftp'][$i]['timeout']); + else + $conn_id = @ftp_ssl_connect($config['ftp'][$i]['server'], $config['ftp'][$i]['port'], $config['ftp'][$i]['timeout']); + if (is_resource($conn_id)){ + $ret[] = sprintf($lang['L_FTP_CONNECTION_SUCCESS'], $config['ftp'][$i]['server'], $config['ftp'][$i]['port']); + }else + $ret[] = sprintf($lang['L_FTP_CONNECTION_ERROR'], $config['ftp'][$i]['server'], $config['ftp'][$i]['port']); + if ($conn_id){ + $login_result = @ftp_login($conn_id, $config['ftp'][$i]['user'], $config['ftp'][$i]['pass']); + if ($login_result) + $ret[] = sprintf($lang['L_FTP_LOGIN_SUCCESS'], $config['ftp'][$i]['user']); + else + $ret[] = sprintf($lang['L_FTP_LOGIN_ERROR'], $config['ftp'][$i]['user']); + } + if ($conn_id && $login_result){ + $pass = 2; + if ($config['ftp'][$i]['mode'] == 1){ + if (ftp_pasv($conn_id, true)) + $ret[] = $lang['L_FTP_PASV_SUCCESS']; + else + $ret[] = $lang['L_FTP_PASV_ERROR']; + } + } + } + if ($pass == 2){ + $dirc = @ftp_chdir($conn_id, $config['ftp'][$i]['dir']); + if (!$dirc){ + $ret[] = $lang['L_CHANGEDIR'] . ' \'' . $config['ftp'][$i]['dir'] . '\' -> ' . $lang['L_CHANGEDIRERROR'] . ''; + }else{ + $pass = 3; + $ret[] = $lang['L_CHANGEDIR'] . ' \'' . $config['ftp'][$i]['dir'] . '\' -> ' . $lang['L_OK'] . ''; + } + @ftp_close($conn_id); + } + if ($pass == 3) + $ret[] = '' . $lang['L_FTP_OK'] . ''; + return implode('
', $ret); +} +/** + * Returns list of configuration profiles as HTML-Optionlist + * + * @param string $selected_config The actual configuration to pre-select in option list + * + * @return string HTML-option-string + */ +function getConfigFilelist($selected_config) +{ + $configs = getConfigFilenames(); + $options = Html::getOptionlist($configs, $selected_config); + return $options; +} +/** + * Returns list of installed themes as HTML-Optionlist + * + * @return string HTML-option-string + */ +function getThemes() +{ + global $config; + $themes = array(); + $dh = opendir($config['paths']['root'] . "css/"); + while (false !== ($filename = readdir($dh))){ + if ($filename != '.' && $filename != '..' && is_dir($config['paths']['root'] . 'css/' . $filename) && substr($filename, 0, 1) != '.' && substr($filename, 0, 1) != '_'){ + $themes[$filename] = $filename; + } + } + @ksort($themes); + $options = Html::getOptionlist($themes, $config['theme']); + return $options; +} +/** + * Detects all language-directories and builds HTML-Optionlist + * + * @return string HTML-option-string + */ +function getLanguageCombo() +{ + global $config, $lang; + $default = $config['language']; + $dh = opendir('./language/'); + $r = ""; + $lang_files = array(); + while (false !== ($filename = readdir($dh))){ + if ($filename != '.' && $filename != '.svn' && $filename != '..' && $filename != 'flags' && is_dir('./language/' . $filename)){ + if (isset($lang[$filename])) + $lang_files[$lang[$filename]] = $filename; + } + } + @ksort($lang_files); + foreach ($lang_files as $filename){ + $style = 'background:url(language/flags/width25/' . $filename . '.gif) 4px;background-repeat:no-repeat;padding:2px 6px 2px 36px !important;'; + $r .= '' . "\n"; + for ($i = 0; $i < count($SQL_ARRAY); $i++) + { + $s = trim(getQueryFromSqlLibrary($i)); + $r .= '