diff --git a/index.php b/index.php index 79a8e29..076b0d9 100644 --- a/index.php +++ b/index.php @@ -1,9 +1,9 @@ . +// along with this program. If not, see . // // //////////////////////////////////////////////////////////////////////// // @@ -133,7 +133,7 @@ function leet_text($value) "srch_again" => "Do Another Search", "login" => "Log In", "logout" => "Logout", - "view" => "View", + "view" => "View", // here, the noun SQL view is meant, not the verb "to view" "confirm" => "Confirm", "cancel" => "Cancel", "save_as" => "Save As", @@ -172,6 +172,8 @@ function leet_text($value) "none" => "None", "as_defined" => "As defined", "expression" => "Expression", + "download" => "Download", + "open_in_browser" => "Open in browser", "sqlite_ext" => "SQLite extension", "sqlite_ext_support" => "It appears that none of the supported SQLite library extensions are available in your installation of PHP. You may not use %s until you install at least one of them.", @@ -199,7 +201,8 @@ function leet_text($value) "db_not_writeable" => "The database, '%s', does not exist and cannot be created because the containing directory, '%s', is not writable. The application is unusable until you make it writable.", "db_setup" => "There was a problem setting up your database, %s. An attempt will be made to find out what's going on so you can fix the problem more easily", "db_exists" => "A database, other file or directory of the name '%s' already exists.", - + "db_blank" => "The database name cannot be blank.", + "exported" => "Exported", "struct" => "Structure", "struct_for" => "structure for", @@ -218,6 +221,7 @@ function leet_text($value) "remember" => "Remember me", "no_db" => "Welcome to %s. It appears that you have selected to scan a directory for databases to manage. However, %s could not find any valid SQLite databases. You may use the form below to create your first database.", "no_db2" => "The directory you specified does not contain any existing databases to manage, and the directory is not writable. This means you can't create any new databases using %s. Either make the directory writable or manually upload databases to the directory.", + "dir_not_executable" => "The directory you specified cannot be scanned for databases as %s has no execute permissions on it. On Linux, use 'chmod +x %s' to fix this.", "create" => "Create", "created" => "has been created", @@ -305,6 +309,7 @@ function leet_text($value) "import_suc" => "Import was successful.", "import_into" => "Import into", "import_f" => "File to import", + "max_file_size" => "Maximum file size", "rename_tbl" => "Rename table '%s' to", "rows_records" => "row(s) starting from record # ", @@ -346,6 +351,7 @@ function leet_text($value) "edit_col" => "Editing column '%s'", "vac" => "Vacuum", "vac_desc" => "Large databases sometimes need to be VACUUMed to reduce their footprint on the server. Click the button below to VACUUM the database '%s'.", + "vac_on_empty"=>"Rebuild database file to recover unused space (Vacuum)", "event" => "Event", "each_row" => "For Each Row", "define_index" => "Define index properties", @@ -356,7 +362,6 @@ function leet_text($value) "desc" => "Descending", "warn0" => "You have been warned.", "warn_passwd" => "You are using the default password, which can be dangerous. You can change it easily at the top of %s.", - "warn_dumbass" => "You didn't change the value dumbass ;-)", "counting_skipped" => "Counting of records has been skipped for some tables because your database is comparably big and some tables don't have primary keys assigned to them so counting might be slow. Add a primary key to these tables or %sforce counting%s.", "sel_state" => "Select Statement", "delimit" => "Delimiter", @@ -370,8 +375,10 @@ function leet_text($value) "db_moved_outside" => "You either tried to move the database into a directory where it cannot be managed anylonger, or the check if you did this failed because of missing rights.", "extension_not_allowed" => "The extension you provided is not within the list of allowed extensions. Please use one of the following extensions", "add_allowed_extension" => "You can add extensions to this list by adding your extension to \$allowed_extensions in the configuration.", + "database_not_writable" => "The database-file is not writable, so its content cannot be changed in any way.", "directory_not_writable" => "The database-file itself is writable, but to write into it, the containing directory needs to be writable as well. This is because SQLite puts temporary files in there for locking.", "tbl_inexistent" => "Table %s does not exist", + "col_inexistent" => "Column %s does not exist", // errors that can happen when ALTER TABLE fails. You don't necessarily have to translate these. "alter_failed" => "Altering of Table %s failed", @@ -405,7 +412,9 @@ function leet_text($value) "help9" => "Add Comments to Exported SQL File", "help9_x" => "During the process for exporting to an SQL file, you may choose to include comments that explain each step of the process so that a human can better understand what is happening.", "help10" => "Partial Indexes", - "help10_x" => "Partial indexes are indexes over a subset of the rows of a table specified by a WHERE clause. Note this requires at least SQLite 3.8.0 and database files with partial indexes won't be readable or writable by older versions. See the SQLite documentation." + "help10_x" => "Partial indexes are indexes over a subset of the rows of a table specified by a WHERE clause. Note this requires at least SQLite 3.8.0 and database files with partial indexes won't be readable or writable by older versions. See the SQLite documentation.", + "help11" => "Maximum size of file uploads", + "help11_x" => "The maximum size of file uploads is determined by three PHP settings: upload_max_filesize, post_max_size and memory_limit. The smallest of these three limits the maximum size for file uploads. To upload larger files, adjust these values in your php.ini file." ); @@ -424,16 +433,18 @@ function leet_text($value) //constants 1 define("PROJECT", "phpLiteAdmin"); -define("VERSION", "1.9.7.1"); -define("PAGE", basename(__FILE__)); +define("VERSION", "1.9.8.2"); define("FORCETYPE", false); //force the extension that will be used (set to false in almost all circumstances except debugging) define("SYSTEMPASSWORD", $password); // Makes things easier. -define('PROJECT_URL','http://www.phpliteadmin.org/'); -define('DONATE_URL','http://www.phpliteadmin.org/donate/'); +define('PROJECT_URL','https://www.phpliteadmin.org/'); +define('DONATE_URL','https://www.phpliteadmin.org/donate/'); define('VERSION_CHECK_URL','https://www.phpliteadmin.org/current_version.php'); define('PROJECT_BUGTRACKER_LINK','https://bitbucket.org/phpliteadmin/public/issues?status=new&status=open'); define('PROJECT_INSTALL_LINK','https://bitbucket.org/phpliteadmin/public/wiki/Installation'); +// up here, we don't output anything. debug output might appear here which is catched by ob and thrown later +ob_start(); + // Resource output (css and javascript files) // we get out of the main code as soon as possible, without inizializing the session if (isset($_GET['resource'])) @@ -445,41 +456,12 @@ function leet_text($value) // don't mess with this - required for the login session ini_set('session.cookie_httponly', '1'); session_start(); -// generate CSRF token -if (empty($_SESSION['token'])) -{ - if (function_exists('random_bytes')) - { - $_SESSION['token'] = bin2hex(random_bytes(32)); - } - elseif (function_exists('mcrypt_create_iv')) - { - $_SESSION['token'] = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); - } else { - $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32)); - } -} -$token = $_SESSION['token']; -$token_html = ''; -// checking CSRF token -if($_SERVER['REQUEST_METHOD'] === 'POST' || isset($_GET['download'])) // all POST forms need tokens! downloads are protected as well -{ - if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['token'])) - $check_token=$_POST['token']; - elseif($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['token'])) - $check_token=$_GET['token']; - - if (!isset($check_token)) - { - die("CSRF token missing"); - } - elseif ((function_exists('hash_equals') && !hash_equals($_SESSION['token'], $check_token)) || - (!function_exists('hash_equals') && $_SESSION['token']!==$check_token) ) // yes, timing attacks might be possible here. update your php ;) - { - die("CSRF token is wrong - please try to login again"); - } -} +// version-number added so after updating, old session-data is not used anylonger +// cookies names cannot contain symbols, except underscores +define("COOKIENAME", preg_replace('/[^a-zA-Z0-9_]/', '_', $cookie_name . '_' . VERSION) ); + +$params = new GetParameters(); if($debug==true) { @@ -503,9 +485,6 @@ function leet_text($value) $lang = array_merge($temp_lang, $lang); unset($temp_lang); } -// version-number added so after updating, old session-data is not used anylonger -// cookies names cannot contain symbols, except underscores -define("COOKIENAME", preg_replace('/[^a-zA-Z0-9_]/', '_', $cookie_name . '_' . VERSION) ); // stripslashes if MAGIC QUOTES is turned on // This is only a workaround. Please better turn off magic quotes! @@ -535,6 +514,23 @@ function leet_text($value) //- Support functions +// for php < 5.6.0 +if(!function_exists('hash_equals')) +{ + function hash_equals($str1, $str2) + { + if(strlen($str1) != strlen($str2)) + return false; + else { + $res = $str1 ^ $str2; + $ret = 0; + for($i = strlen($res) - 1; $i >= 0; $i--) + $ret |= ord($res[$i]); + return !$ret; + } + } +} + //function that allows SQL delimiter to be ignored inside comments or strings function explode_sql($delimiter, $sql) { @@ -581,7 +577,7 @@ function explode_sql($delimiter, $sql) $i++; } continue 2; - } + } } $i++; } @@ -593,8 +589,8 @@ function explode_sql($delimiter, $sql) //function to scan entire directory tree and subdirectories function dir_tree($dir) { - $path = ''; - $stack[] = $dir; + $path = array(); + $stack = array($dir); while($stack) { $thisdir = array_pop($stack); @@ -627,7 +623,7 @@ function dir_tree($dir) function helpLink($name) { global $lang; - return "[?]"; + return "[?]"; } // function to encode value into HTML just like htmlentities, but with adjusted default settings @@ -636,25 +632,60 @@ function htmlencode($value, $flags=ENT_QUOTES, $encoding ="UTF-8") return htmlentities($value, $flags, $encoding); } -// 22 August 2011: gkf added this function to support display of -// default values in the form used to INSERT new data. -function deQuoteSQL($s) -{ - return trim(trim($s), "'"); -} - // reduce string chars function subString($str) { - global $charsNum; - if($charsNum > 10 && (!isset($_SESSION[COOKIENAME.'fulltexts']) || !$_SESSION[COOKIENAME.'fulltexts']) && strlen($str)>$charsNum) + global $charsNum, $params; + if($charsNum > 10 && (!isset($params->fulltexts) || !$params->fulltexts) && mb_strlen($str)>$charsNum) { - $str = substr($str, 0, $charsNum).'...'; + $str = mb_substr($str, 0, $charsNum).'...'; } return $str; } -// checks the (new) name of a database file +// marks searchwords and htmlencodes correctly +function markSearchWords($input, $field, $search) +{ + $output = htmlencode($input); + if(isset($search['values'][$field]) && is_array($search['values'][$field])) + { + // build one regex that matches (all) search words + $regex = '/'; + $vali=0; + foreach($search['values'][$field] as $searchValue) + { + if($search['operators'][$field] =='LIKE' || $search['operators'][$field] == 'LIKE%') + $regex .= '(?:'.($searchValue[0]=='%'?'':'^'); // does the searchvalue have to occur at the start? + $regex .= preg_quote(trim($searchValue,'%'),'/'); // the search value + if($search['operators'][$field] =='LIKE' || $search['operators'][$field] == 'LIKE%') + $regex .= (substr($searchValue,-1)=='%'?'':'$').')'; // does the searchvalue have to occur at the end? + if($vali++ $betweenPart) + { + $output .= htmlencode($betweenPart); // part that does not match (might be empty) + if(isset($fldFoundParts[0][$index])) + $output .= ''.htmlencode($fldFoundParts[0][$index]).''; // the part that matched + } + } + return $output; +} + +// checks the (new) name of a database file function checkDbName($name) { global $allowed_extensions; @@ -677,7 +708,7 @@ function isManagedDB($path) global $databases; foreach($databases as $db_key => $database) { - if($path == $database['path']) + if($path === $database['path']) { // a db we manage. Thats okay. // return the key. @@ -689,7 +720,7 @@ function isManagedDB($path) } // from a typename of a colun, get the type of the column's affinty -// see http://www.sqlite.org/datatype3.html section 2.1 for rules +// see https://www.sqlite.org/datatype3.html section 2.1 for rules function get_type_affinity($type) { if (preg_match("/INT/i", $type)) @@ -705,15 +736,55 @@ function get_type_affinity($type) } +// Returns a file size limit in bytes based on the PHP upload_max_filesize +// post_max_size and memory_limit. Returns -1 in case of no limit. +function fileUploadMaxSize() +{ + $max1 = parseSize(ini_get('post_max_size')); + $max2 = parseSize(ini_get('upload_max_filesize')); + $max3 = parseSize(ini_get('memory_limit')); + if($max1>0 && ($max1<=$max2 || $max2==0) && ($max1<=$max3 || $max3==-1)) + return $max1; + elseif($max2>0 && ($max2<=$max1 || $max1==0) && ($max2<=$max3 || $max3==-1)) + return $max2; + elseif($max3>-1 && ($max3<=$max1 || $max1==0) && ($max3<=$max2 || $max2==0)) + return $max3; + else + return -1; // no limit +} + +// Parses given size string like "12M" into number of bytes +// based on https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Component%21Utility%21Bytes.php/function/Bytes%3A%3AtoInt/8.2.x +function parseSize($size) +{ + // Remove the non-unit characters from the size. + $unit = preg_replace('/[^bkmgtpezy]/i', '', $size); + // Remove the non-numeric characters from the size. + $size = preg_replace('/[^0-9\.]/', '', $size); + if ($unit) + { + // Find the position of the unit in the ordered string which is the power + // of magnitude to multiply a kilobyte by. + return round($size * pow(1024, stripos('bkmgtpezy', $unit[0]))); + } + else { + return round($size); + } +} + + //- Check user authentication, login and logout $auth = new Authorization(); //create authorization object // check if user has attempted to log out -if (isset($_POST['logout'])) +if (isset($_GET['logout'])) $auth->revoke(); // check if user has attempted to log in else if (isset($_POST['login']) && isset($_POST['password'])) - $auth->attemptGrant($_POST['password'], isset($_POST['remember'])); +{ + $attempt = $auth->attemptGrant($_POST['password'], isset($_POST['remember'])); + $params->redirect( $attempt ? array():array('failed'=>'1') ); +} //- Actions on database files and bulk data if ($auth->isAuthorized()) @@ -723,17 +794,15 @@ function get_type_affinity($type) if(isset($_POST['new_dbname'])) { if($_POST['new_dbname']=='') - { - // TODO: Display an error message (do NOT echo here. echo below in the html-body!) - } + $params->redirect(array('table'=>null), $lang['err'].': '.$lang['db_blank']); else { - $str = preg_replace('@[^\w-.]@','', $_POST['new_dbname']); + $str = preg_replace('@[^\w\-.]@u','', $_POST['new_dbname']); $dbname = $str; $dbpath = $str; if(checkDbName($dbname)) { - $tdata = array(); + $tdata = array(); $tdata['name'] = $dbname; $tdata['path'] = $directory.DIRECTORY_SEPARATOR.$dbpath; if(isset($_POST['new_dbtype'])) @@ -744,18 +813,20 @@ function get_type_affinity($type) $td->query("VACUUM"); } else { - if(is_file($dbname) || is_dir($dbname)) $dbexists = true; - else $extension_not_allowed=true; + if(is_file($dbname) || is_dir($dbname)) + $params->redirect(array('view'=>'structure'),$lang['err'].': '.sprintf($lang['db_exists'], htmlencode($dbname))); + else + $params->redirect(array('view'=>'structure'),$lang['extension_not_allowed'].': '.implode(', ', array_map('htmlencode', $allowed_extensions)).'
'.$lang['add_allowed_extension']); } } } - + //- Scan a directory for databases if($directory!==false) { if($directory[strlen($directory)-1]==DIRECTORY_SEPARATOR) //if user has a trailing slash in the directory, remove it $directory = substr($directory, 0, strlen($directory)-1); - + if(is_dir($directory)) //make sure the directory is valid { if($subdirectories===true) @@ -768,7 +839,7 @@ function get_type_affinity($type) { if($subdirectories===false) $arr[$i] = $directory.DIRECTORY_SEPARATOR.$arr[$i]; - + if(@!is_file($arr[$i])) continue; $con = file_get_contents($arr[$i], NULL, NULL, 0, 60); if(strpos($con, "** This file contains an SQLite 2.1 database **", 0)!==false || strpos($con, "SQLite format 3", 0)!==false) @@ -790,9 +861,10 @@ function get_type_affinity($type) { foreach($databases as $db_id => $database) { - if($database['path'] == $tdata['path']) + if($database['path'] === $tdata['path']) { - $_SESSION[COOKIENAME.'currentDB'] = $database; + $currentDB = $database; + $params->database = $database['path']; break; } } @@ -815,7 +887,7 @@ function get_type_affinity($type) $databases[$i]['writable_dir'] = is_writable(dirname($databases[$i]['path'])); $databases[$i]['readable'] = is_writable(dirname($databases[$i]['path'])); } - else + else { $databases[$i]['writable'] = is_writable($databases[$i]['path']); $databases[$i]['writable_dir'] = is_writable(dirname($databases[$i]['path'])); @@ -824,10 +896,17 @@ function get_type_affinity($type) } sort($databases); } - // we now have the $databases array set. Check whethet currentDB is a managed Db (is in this array) - if(isset($_SESSION[COOKIENAME.'currentDB']) && isManagedDB($_SESSION[COOKIENAME.'currentDB']['path']) === false) - unset($_SESSION[COOKIENAME.'currentDB']); - + // we now have the $databases array set. Check whether selected DB is a managed Db (is in this array) + if(!isset($currentDB) && (isset($_GET['database']) || isset($_POST['database']) ) ) + { + $selected_db = ( isset($_POST['database']) ? $_POST['database'] : $_GET['database'] ); + $db_key = isManagedDB($selected_db); + if($db_key!==false) { + $currentDB = $databases[$db_key]; + $params->database = $databases[$db_key]['path']; + } + } + //- Delete an existing database if(isset($_GET['database_delete'])) { @@ -837,11 +916,12 @@ function get_type_affinity($type) if($checkDB !== false) { unlink($dbpath); - unset($_SESSION[COOKIENAME.'currentDB']); + unset($params->database); + unset($currentDB); unset($databases[$checkDB]); } else die($lang['err'].': '.$lang['delete_only_managed']); } - + //- Rename an existing database if(isset($_GET['database_rename'])) { @@ -862,9 +942,9 @@ function get_type_affinity($type) // its okay, the new directory is within $directory $newpath = $_POST['newname']; } - else die($lang['err'].': '.$lang['db_moved_outside']); + else $params->redirect(array('view'=>'rename'), $lang['err'].': '.$lang['db_moved_outside']); } - + if(checkDbName($newpath)) { $checkDB = isManagedDB($oldpath); @@ -873,22 +953,26 @@ function get_type_affinity($type) rename($oldpath, $newpath); $databases[$checkDB]['path'] = $newpath; $databases[$checkDB]['name'] = basename($newpath); - $_SESSION[COOKIENAME.'currentDB'] = $databases[$checkDB]; - $justrenamed = true; + $currentDB = $databases[$checkDB]; + $params->database = $databases[$checkDB]['path']; + $params->redirect(array('view'=>'rename'), sprintf($lang['db_renamed'], htmlencode($oldpath))." '".htmlencode($newpath)."'."); } - else die($lang['err'].': '.$lang['rename_only_managed']); + else $params->redirect(array('view'=>'rename'), $lang['err'].': '.$lang['rename_only_managed']); } else { - if(is_file($newpath) || is_dir($newpath)) $dbexists = true; - else $extension_not_allowed = true; + if(is_file($newpath) || is_dir($newpath)) + $params->redirect(array('view'=>'rename'), $lang['err'].": " . sprintf($lang['db_exists'], htmlencode($newpath))); + else + $params->redirect(array('view'=>'rename'), $lang['err'].": " . $lang['extension_not_allowed'].': '.implode(', ', array_map('htmlencode', $allowed_extensions)).'
'.$lang['add_allowed_extension']); } } - + //- Export (download a dump) an existing database if(isset($_POST['export'])) { + ob_end_clean(); $export_filename = str_replace(array("\r", "\n"), '',$_POST['filename']); // against http header injection (php < 5.1.2 only) if($_POST['export_type']=="sql") { @@ -906,7 +990,7 @@ function get_type_affinity($type) $data = isset($_POST['data']); $transaction = isset($_POST['transaction']); $comments = isset($_POST['comments']); - $db = new Database($_SESSION[COOKIENAME.'currentDB']); + $db = new Database($currentDB); echo $db->export_sql($tables, $drop, $structure, $data, $transaction, $comments); } else if($_POST['export_type']=="csv") @@ -928,16 +1012,16 @@ function get_type_affinity($type) $null = $_POST['export_csv_replacenull']; $crlf = isset($_POST['export_csv_crlf']); $fields_in_first_row = isset($_POST['export_csv_fieldnames']); - $db = new Database($_SESSION[COOKIENAME.'currentDB']); + $db = new Database($currentDB); echo $db->export_csv($tables, $field_terminate, $field_enclosed, $field_escaped, $null, $crlf, $fields_in_first_row); } exit(); } - + //- Import a file into an existing database if(isset($_POST['import'])) { - $db = new Database($_SESSION[COOKIENAME.'currentDB']); + $db = new Database($currentDB); $db->registerUserFunction($custom_functions); if($_POST['import_type']=="sql") { @@ -951,12 +1035,28 @@ function get_type_affinity($type) $field_escaped = $_POST['import_csv_fieldsescaped']; $null = $_POST['import_csv_replacenull']; $fields_in_first_row = isset($_POST['import_csv_fieldnames']); - $importSuccess = $db->import_csv($_FILES["file"]["tmp_name"], $_POST['single_table'], $field_terminate, $field_enclosed, $field_escaped, $null, $fields_in_first_row); + if(isset($_POST['single_table']) && $_POST['single_table']!='') + $table = $_POST['single_table']; + else + { + $table = basename($_FILES["file"]["name"],".csv"); + $i=""; + while($db->getTypeOfTable($table.$i)!="") + { + if($i=="") + $i=2; + else + $i++; + } + $table = $table.$i; + } + $importSuccess = $db->import_csv($_FILES["file"]["tmp_name"], $table, $field_terminate, $field_enclosed, $field_escaped, $null, $fields_in_first_row); } } //- Download (backup) a database file (as SQLite file, not as dump) if(isset($_GET['download']) && isManagedDB($_GET['download'])!==false) { + ob_end_clean(); header("Content-type: application/octet-stream"); header('Content-Disposition: attachment; filename="'.basename($_GET['download']).'";'); header("Pragma: no-cache"); @@ -964,745 +1064,962 @@ function get_type_affinity($type) readfile($_GET['download']); exit; } -} - -//- HTML: output starts here -header('Content-Type: text/html; charset=utf-8'); -?> - - - - - - -<?php echo PROJECT ?> - -", PHP_EOL; -else - // only use the default stylesheet if an external one does not exist - echo "", PHP_EOL; -// HTML: output help text, then exit -if(isset($_GET['help'])) -{ - //help section array - $help = array - ( - $lang['help1'] => sprintf($lang['help1_x'], PROJECT, PROJECT, PROJECT), $lang['help2'] => $lang['help2_x'], $lang['help3'] => $lang['help3_x'], - $lang['help4'] => $lang['help4_x'], $lang['help5'] => $lang['help5_x'], $lang['help6'] => $lang['help6_x'], - $lang['help7'] => $lang['help7_x'], $lang['help8'] => $lang['help8_x'], $lang['help9'] => $lang['help9_x'], $lang['help10'] => $lang['help10_x'] - ); - ?> - - -
- "; - echo "".PROJECT." v".VERSION." ".$lang['help_doc']."

"; - foreach((array)$help as $key => $val) + //- Select database (from session or first available) + if(!isset($currentDB) && count($databases)>0) { - echo "".$key."
"; + //set the current database to the first existing one in the array (default) + $currentDB = reset($databases); + $params->database = $currentDB['path']; } - echo "
"; - echo "

"; - foreach((array)$help as $key => $val) + + if(isset($currentDB)) { - echo "
"; - echo "".$key.""; - echo "
"; - echo $val; - echo "
"; - echo "".$lang['back_top'].""; - echo "
"; + //- Open database (creates a Database object) + $db = new Database($currentDB); //create the Database object + $db->registerUserFunction($custom_functions); } - ?> - - - - - - - - -".$lang['bad_php_directive'].""; - echo ""; - exit(); -} - -//- HTML: login screen if not authorized, exit -if(!$auth->isAuthorized()) -{ - echo "
"; - echo "

v".VERSION."

"; - echo "
"; - if ($auth->isFailedLogin()) - echo "".$lang['passwd_incorrect']."

"; - echo "
"; - echo $token_html; - echo $lang['passwd'].":
"; - echo "

"; - echo ""; - echo ""; - echo "
"; - echo "
"; - echo "
"; - echo "
"; - echo "
"; - echo "".$lang['powered']." ".PROJECT." | "; - printf($lang['page_gen'], $pageTimer); - echo "
"; - echo ""; - exit(); -} -//- User is authorized, display the main application + // collect parameters early, just once + $target_table = isset($_GET['table']) ? $_GET['table'] : null; + // are we working on a view? let's check once here + $target_table_type = !is_null($target_table) ? $db->getTypeOfTable($target_table) : null; + if(is_null($target_table_type) && !is_null($target_table)) + $params->redirect(array('table'=>null), $lang['err'].': '.sprintf($lang['tbl_inexistent'], htmlencode($target_table))); + $params->table = $target_table; + + // initialize / change fulltexts and numrows parameter + if(isset($_GET['fulltexts'])) + $params->fulltexts = ($_GET['fulltexts'] ? 1 : 0); + else + $params->fulltexts = 0; -//- Select database (from session or first available) -if(!isset($_SESSION[COOKIENAME.'currentDB']) && count($databases)>0) -{ - //set the current database to the first existing one in the array (default) - $_SESSION[COOKIENAME.'currentDB'] = reset($databases); -} -if(sizeof($databases)>0) - $currentDB = $_SESSION[COOKIENAME.'currentDB']; -else // the database array is empty, offer to create a new database -{ - //- HTML: form to create a new database, exit - if($directory!==false && is_writable($directory)) - { - echo "
"; - printf($lang['no_db'], PROJECT, PROJECT); - echo "
"; - if(isset($extension_not_allowed)) - { - echo "
"; - echo $lang['err'].': '.$lang['extension_not_allowed'].': '; - echo implode(', ', array_map('htmlencode', $allowed_extensions)); - echo '
'.$lang['add_allowed_extension']; - echo "

"; - } - echo "
".$lang['db_create'].""; - echo "
"; - echo $token_html; - echo " "; - if(class_exists('SQLiteDatabase') && (class_exists('SQLite3') || class_exists('PDO'))) - { - echo ""; - } - echo ""; - echo "
"; - echo "
"; - } + if(isset($_GET['numRows']) && intval($_GET['numRows'])>0) + $params->numRows = intval($_GET['numRows']); else - { - echo "
"; - echo $lang['err'].": ".sprintf($lang['no_db2'], PROJECT); - echo "

"; - } - exit(); -} + $params->numRows = $rowsNum; -//- Switch to a different database with drop-down menu -if(isset($_POST['database_switch'])) -{ - foreach($databases as $db_id => $database) + //- Switch on $_GET['action'] for operations without output + if(isset($_GET['action']) && isset($_GET['confirm'])) { - if($database['path'] == $_POST['database_switch']) + switch($_GET['action']) { - $_SESSION[COOKIENAME."currentDB"] = $database; - break; - } - } - $currentDB = $_SESSION[COOKIENAME.'currentDB']; -} -else if(isset($_GET['switchdb'])) -{ - foreach($databases as $db_id => $database) - { - if($database['path'] == $_GET['switchdb']) - { - $_SESSION[COOKIENAME."currentDB"] = $database; - break; - } - } - $currentDB = $_SESSION[COOKIENAME.'currentDB']; -} -if(isset($_SESSION[COOKIENAME.'currentDB']) && in_array($_SESSION[COOKIENAME.'currentDB'], $databases)) - $currentDB = $_SESSION[COOKIENAME.'currentDB']; - -//- Open database (creates a Database object) -$db = new Database($currentDB); //create the Database object -$db->registerUserFunction($custom_functions); - -// collect parameters early, just once -$target_table = isset($_GET['table']) ? $_GET['table'] : null; - -//- Switch on $_GET['action'] for operations without output -if(isset($_GET['action']) && isset($_GET['confirm'])) -{ - switch($_GET['action']) - { - //- Table actions + //- Table actions - //- Create table (=table_create) - case "table_create": - $num = intval($_POST['rows']); - $name = $_POST['tablename']; - $primary_keys = array(); - for($i=0; $i<$num; $i++) - { - if($_POST[$i.'_field']!="" && isset($_POST[$i.'_primarykey'])) + //- Create table (=table_create) + case "table_create": + $num = intval($_POST['rows']); + $name = $_POST['tablename']; + $primary_keys = array(); + for($i=0; $i<$num; $i++) { - $primary_keys[] = $_POST[$i.'_field']; + if($_POST[$i.'_field']!="" && isset($_POST[$i.'_primarykey'])) + { + $primary_keys[] = $_POST[$i.'_field']; + } } - } - $query = "CREATE TABLE ".$db->quote($name)." ("; - for($i=0; $i<$num; $i++) - { - if($_POST[$i.'_field']!="") + $query = "CREATE TABLE ".$db->quote($name)." ("; + for($i=0; $i<$num; $i++) { - $query .= $db->quote($_POST[$i.'_field'])." "; - $query .= $_POST[$i.'_type']." "; - if(isset($_POST[$i.'_primarykey'])) + if($_POST[$i.'_field']!="") { - if(count($primary_keys)==1) + $query .= $db->quote($_POST[$i.'_field'])." "; + $query .= $_POST[$i.'_type']." "; + if(isset($_POST[$i.'_primarykey'])) + { + if(count($primary_keys)==1) + { + $query .= "PRIMARY KEY "; + if(isset($_POST[$i.'_autoincrement']) && $db->getType() != "SQLiteDatabase") + $query .= "AUTOINCREMENT "; + } + $query .= "NOT NULL "; + } + if(!isset($_POST[$i.'_primarykey']) && isset($_POST[$i.'_notnull'])) + $query .= "NOT NULL "; + if($_POST[$i.'_defaultoption']!='defined' && $_POST[$i.'_defaultoption']!='none' && $_POST[$i.'_defaultoption']!='expr') + $query .= "DEFAULT ".$_POST[$i.'_defaultoption']." "; + elseif($_POST[$i.'_defaultoption']=='expr') + $query .= "DEFAULT (".$_POST[$i.'_defaultvalue'].") "; + elseif(isset($_POST[$i.'_defaultvalue']) && $_POST[$i.'_defaultoption']=='defined') { - $query .= "PRIMARY KEY "; - if(isset($_POST[$i.'_autoincrement']) && $db->getType() != "SQLiteDatabase") - $query .= "AUTOINCREMENT "; + $typeAffinity = get_type_affinity($_POST[$i.'_type']); + if(($typeAffinity=="INTEGER" || $typeAffinity=="REAL" || $typeAffinity=="NUMERIC") && is_numeric($_POST[$i.'_defaultvalue'])) + $query .= "DEFAULT ".$_POST[$i.'_defaultvalue']." "; + else + $query .= "DEFAULT ".$db->quote($_POST[$i.'_defaultvalue'])." "; } - $query .= "NOT NULL "; + $query = substr($query, 0, -1); + $query .= ", "; } - if(!isset($_POST[$i.'_primarykey']) && isset($_POST[$i.'_notnull'])) - $query .= "NOT NULL "; - if($_POST[$i.'_defaultoption']!='defined' && $_POST[$i.'_defaultoption']!='none' && $_POST[$i.'_defaultoption']!='expr') - $query .= "DEFAULT ".$_POST[$i.'_defaultoption']." "; - elseif($_POST[$i.'_defaultoption']=='expr') - $query .= "DEFAULT (".$_POST[$i.'_defaultvalue'].") "; - elseif(isset($_POST[$i.'_defaultvalue']) && $_POST[$i.'_defaultoption']=='defined') + } + if (count($primary_keys)>1) + { + $compound_key = ""; + foreach ($primary_keys as $primary_key) { - $typeAffinity = get_type_affinity($_POST[$i.'_type']); - if(($typeAffinity=="INTEGER" || $typeAffinity=="REAL" || $typeAffinity=="NUMERIC") && is_numeric($_POST[$i.'_defaultvalue'])) - $query .= "DEFAULT ".$_POST[$i.'_defaultvalue']." "; - else - $query .= "DEFAULT ".$db->quote($_POST[$i.'_defaultvalue'])." "; + $compound_key .= ($compound_key=="" ? "" : ", ") . $db->quote($primary_key); } - $query = substr($query, 0, sizeof($query)-2); - $query .= ", "; + $query .= "PRIMARY KEY (".$compound_key."), "; } - } - if (count($primary_keys)>1) - { - $compound_key = ""; - foreach ($primary_keys as $primary_key) + $query = substr($query, 0, -2); + $query .= ")"; + $result = $db->query($query); + if($result === false) + $completed = $db->getError(true); + else + $completed = $lang['tbl']." '".htmlencode($_POST['tablename'])."' ".$lang['created'].".
".htmlencode($query).""; + $params->redirect(($result===false ? array() : array('action'=>'column_view', 'table'=>$name) ), $completed); + break; + + //- Empty table (=table_empty) + case "table_empty": + $query1 = "DELETE FROM ".$db->quote_id($_GET['table']).";"; + $result1 = $db->query($query1); + if($result1 === false) + $completed = $db->getError(true); + if(isset($_POST['vacuum']) && $_POST['vacuum']) { - $compound_key .= ($compound_key=="" ? "" : ", ") . $db->quote($primary_key); + $query2 = "VACUUM;"; + $result2 = $db->query($query2); } - $query .= "PRIMARY KEY (".$compound_key."), "; - } - $query = substr($query, 0, sizeof($query)-3); - $query .= ")"; - $result = $db->query($query); - if($result===false) - $error = true; - $completed = $lang['tbl']." '".htmlencode($_POST['tablename'])."' ".$lang['created'].".
".htmlencode($query).""; - $backlinkParameters = "&action=column_view&table=".urlencode($name); - break; - - //- Empty table (=table_empty) - case "table_empty": - $query = "DELETE FROM ".$db->quote_id($_POST['tablename']); - $result = $db->query($query); - if($result===false) - $error = true; - $query = "VACUUM"; - $result = $db->query($query); - if($result===false) - $error = true; - $completed = $lang['tbl']." '".htmlencode($_POST['tablename'])."' ".$lang['emptied'].".
".htmlencode($query).""; - $backlinkParameters = "&action=row_view&table=".urlencode($name); - break; + else + $query2 = ""; + if($result1 !== false) + $completed = $lang['tbl']." '".htmlencode($_GET['table'])."' ".$lang['emptied'].".
".htmlencode($query1)."
".htmlencode($query2)."
"; + $params->redirect(($result1===false ? array() : array('action'=>'row_view') ), $completed); + break; + + //- Create view (=view_create) + case "view_create": + $query = "CREATE VIEW ".$db->quote($_POST['viewname'])." AS ".$_POST['select']; + $result = $db->query($query); + if($result === false) + $completed = $db->getError(true); + else + $completed = $lang['view']." '".htmlencode($_POST['viewname'])."' ".$lang['created'].".
".htmlencode($query).""; + $params->redirect(($result===false ? array() : array('action'=>'column_view', 'table'=>$_POST['viewname']) ), $completed); + break; + + //- Drop table (=table_drop) + case "table_drop": + $query1 = "DROP TABLE ".$db->quote_id($_GET['table']).";"; + $result1=$db->query($query1); + if($result1 === false) + $completed = $db->getError(true); + if(isset($_POST['vacuum']) && $_POST['vacuum']) + { + $query2 = "VACUUM;"; + $result2 = $db->query($query2); + } + else + $query2 = ""; + if($result1 !== false) + { + $target_table = null; + $completed = $lang['tbl']." '".htmlencode($_GET['table'])."' ".$lang['dropped'].".
".htmlencode($query1)."
".htmlencode($query2)."
";; + } + $params->redirect(array('table'=>null), $completed); + break; + + //- Drop view (=view_drop) + case "view_drop": + $query = "DROP VIEW ".$db->quote_id($_POST['viewname']); + $result=$db->query($query); + if($result === false) + $completed = $db->getError(true); + else + $completed = $lang['view']." '".htmlencode($_POST['viewname'])."' ".$lang['dropped'].".
".htmlencode($query).""; + $params->redirect(array('table'=>null), $completed); + break; + + //- Rename table (=table_rename) + case "table_rename": + $query = "ALTER TABLE ".$db->quote_id($_GET['table'])." RENAME TO ".$db->quote($_POST['newname']); + $type = $db->getTypeOfTable($_GET['table']); + if($db->getVersion()==3 && $type=='table' // SQLite 3 can rename tables, not views + // In SQL(ite) table names are case-insensitve, so changing is not supported by SQLite. + // But table names are stored and displayed case sensitive, so we use the workaround for case sensitive renaming. + && !($_GET['table'] !== $_POST['newname'] && strtolower($_GET['table']) === strtolower($_POST['newname'])) + ) + $result = $db->query($query, true); + else + // Workaround can rename tables of sqlite2 and views of both sqlite versions. Can also do case sensitive renames. + $result = $db->query($query, false); + if($result === false) + $completed = $db->getError(true); + else + { + $completed = $lang['tbl']." '".htmlencode($_GET['table'])."' ".$lang['renamed']." '".htmlencode($_POST['newname'])."'.
".htmlencode($query).""; + $target_table = $_POST['newname']; + } + $params->redirect(array('action'=>'row_view', 'table'=>$_POST['newname']), $completed); + break; - //- Create view (=view_create) - case "view_create": - $query = "CREATE VIEW ".$db->quote($_POST['viewname'])." AS ".$_POST['select']; - $result = $db->query($query); - if($result===false) - $error = true; - $completed = $lang['view']." '".htmlencode($_POST['viewname'])."' ".$lang['created'].".
".htmlencode($query).""; - $backlinkParameters = "&action=column_view&table=".urlencode($_POST['viewname']); - break; + //- Search table (=table_search) + case "table_search": + $searchValues = array(); + $searchOperators = array(); - //- Drop table (=table_drop) - case "table_drop": - $query = "DROP TABLE ".$db->quote_id($_POST['tablename']); - $result=$db->query($query); - if($result===false) - $error = true; - $completed = $lang['tbl']." '".htmlencode($_POST['tablename'])."' ".$lang['dropped']."."; - $backlinkParameters = ""; + $tableInfo = $db->getTableInfo($target_table); + $j = 0; + $whereExpr = array(); + for($i=0; $iquote_id($field)." ".$operator; + else{ + if($operator == "LIKE%"){ + $operator = "LIKE"; + if(!preg_match('/(^%)|(%$)/', $value)) $value = '%'.$value.'%'; + $searchValues[$field] = array($value); + $valueQuoted = $db->quote($value); + } + elseif($operator == 'IN' || $operator == 'NOT IN') + { + $value = trim($value, '() '); + $values = explode(',',$value); + $values = array_map('trim', $values, array_fill(0,count($values),' \'"')); + if($operator == 'IN') + $searchValues[$field] = $values; + $values = array_map(array($db, 'quote'), $values); + $valueQuoted = '(' .implode(', ', $values) . ')'; + } + else + { + $searchValues[$field] = array($value); + $valueQuoted = $db->quote($value); + } + $whereExpr[$j] = $db->quote_id($field)." ".$operator." ".$valueQuoted; + } + $j++; + } + } + $searchWhere = ''; + if(sizeof($whereExpr)>0) + { + $searchWhere .= " WHERE ".$whereExpr[0]; + for($i=1; $i $searchWhere, + 'values' => $searchValues, + 'operators' => $searchOperators + ); + $params->redirect(array('action'=>'table_search','search'=>$searchID)); break; - //- Drop view (=view_drop) - case "view_drop": - $query = "DROP VIEW ".$db->quote_id($_POST['viewname']); - $result=$db->query($query); - if($result===false) - $error = true; - $completed = $lang['view']." '".htmlencode($_POST['viewname'])."' ".$lang['dropped']."."; - $backlinkParameters = ""; - break; + //- Row actions - //- Rename table (=table_rename) - case "table_rename": - $query = "ALTER TABLE ".$db->quote_id($_POST['oldname'])." RENAME TO ".$db->quote($_POST['newname']); - if($db->getVersion()==3) - $result = $db->query($query, true); - else - $result = $db->query($query, false); - if($result===false) - $error = true; - $completed = $lang['tbl']." '".htmlencode($_POST['oldname'])."' ".$lang['renamed']." '".htmlencode($_POST['newname'])."'.
".htmlencode($query).""; - $backlinkParameters = "&action=row_view&table=".urlencode($_POST['newname']); - break; + //- Create row (=row_create) + case "row_create": + $completed = ""; + $num = $_POST['newRows']; + $z = 0; + $error = false; - //- Row actions + $tableInfo = $db->getTableInfo($target_table); - //- Create row (=row_create) - case "row_create": - $completed = ""; - $num = $_POST['numRows']; - $fields = explode(":", $_POST['fields']); - $z = 0; - - $query = "PRAGMA table_info(".$db->quote_id($target_table).")"; - $result = $db->selectArray($query); - - for($i=0; $i<$num; $i++) - { - if(!isset($_POST[$i.":ignore"])) + for($i=0; $i<$num; $i++) { - $query_cols = ""; - $query_vals = ""; - $all_default = true; - for($j=0; $jquote_id($fields[$j]).","; - - $type = $result[$j]['type']; - $typeAffinity = get_type_affinity($type); - $function = $_POST["function_".$i."_".$j]; - if($function!="") - $query_vals .= $function."("; - if(($typeAffinity=="TEXT" || $typeAffinity=="NONE") && !$null) - $query_vals .= $db->quote($value); - elseif(($typeAffinity=="INTEGER" || $typeAffinity=="REAL"|| $typeAffinity=="NUMERIC") && $value=="") - $query_vals .= "NULL"; - elseif($null) - $query_vals .= "NULL"; + $query_cols .= $db->quote_id($tableInfo[$j]['name']).","; + + $function = $_POST["function_".$j][$i]; + if($function!="") + $query_vals .= $function."("; + if(preg_match('/^BLOB/', $type)) + $query_vals .= ':blobval'.$j; + elseif(($typeAffinity=="TEXT" || $typeAffinity=="NONE") && !$null) + $query_vals .= $db->quote($value); + elseif(($typeAffinity=="INTEGER" || $typeAffinity=="REAL"|| $typeAffinity=="NUMERIC") && $value=="") + $query_vals .= "NULL"; + elseif($null) + $query_vals .= "NULL"; + else + $query_vals .= $db->quote($value); + if($function!="") + $query_vals .= ")"; + $query_vals .= ","; + } + $query = "INSERT INTO ".$db->quote_id($target_table); + if(!$all_default) + { + $query_cols = substr($query_cols, 0, strlen($query_cols)-1); + $query_vals = substr($query_vals, 0, strlen($query_vals)-1); + + $query.=" (". $query_cols . ") VALUES (". $query_vals. ")"; + } else { + $query .= " DEFAULT VALUES"; + } + if(isset($blobFiles)) + { + // blob files need to be done using a prepared statement because the query size would be too large + $handle = $db->prepareQuery($query); + foreach($blobFiles as $j=>$filename) + $db->bindValue($handle, ':blobval'.$j, file_get_contents($filename), 'blob'); + + $result1 = $db->executePrepared($handle, false); + } else - $query_vals .= $db->quote($value); - if($function!="") - $query_vals .= ")"; - $query_vals .= ","; - } - $query = "INSERT INTO ".$db->quote_id($target_table); - if(!$all_default) - { - $query_cols = substr($query_cols, 0, strlen($query_cols)-1); - $query_vals = substr($query_vals, 0, strlen($query_vals)-1); - - $query.=" (". $query_cols . ") VALUES (". $query_vals. ")"; - } else { - $query .= " DEFAULT VALUES"; + $result1 = $db->query($query); + if($result1===false) + $error = true; + $completed .= "".htmlencode($query)."
"; + $z++; } - $result1 = $db->query($query); - if($result1===false) - $error = true; - $completed .= "".htmlencode($query)."
"; - $z++; } - } - $completed = $z." ".$lang['rows']." ".$lang['inserted'].".

".$completed; - $backlinkParameters = "&action=column_view&table=".urlencode($target_table); - break; + if($error) + $completed = $db->getError(true); + else + $completed = $z." ".$lang['rows']." ".$lang['inserted'].".

".$completed; + $params->redirect(array('action'=>'row_view'), $completed); + break; - //- Delete row (=row_delete) - case "row_delete": - $pks = json_decode($_GET['pk']); - - $query = "DELETE FROM ".$db->quote_id($target_table)." WHERE (".$db->wherePK($target_table,json_decode($pks[0])).")"; - for($i=1; $iwherePK($target_table,json_decode($pks[$i])).")"; - } - $result = $db->query($query); - if($result===false) - $error = true; - $completed = sizeof($pks)." ".$lang['rows']." ".$lang['deleted'].".
".htmlencode($query).""; - $backlinkParameters = "&action=row_view&table=".urlencode($target_table); - break; + //- Delete row (=row_delete) + case "row_delete": + $pks = json_decode($_GET['pk']); - //- Edit row (=row_edit) - case "row_edit": - $pks = json_decode($_GET['pk']); - $fields = explode(":", $_POST['fieldArray']); - - $z = 0; - - $query = "PRAGMA table_info(".$db->quote_id($target_table).")"; - $result = $db->selectArray($query); - - if(isset($_POST['new_row'])) - $completed = ""; - else - $completed = sizeof($pks)." ".$lang['rows']." ".$lang['affected'].".

"; + $query = "DELETE FROM ".$db->quote_id($target_table)." WHERE (".$db->wherePK($target_table,json_decode($pks[0])).")"; + for($i=1; $iwherePK($target_table,json_decode($pks[$i])).")"; + } + $result = $db->query($query); + if($result === false) + $completed = $db->getError(true); + else + $completed = sizeof($pks)." ".$lang['rows']." ".$lang['deleted'].".
".htmlencode($query).""; + $params->redirect(array('action'=>'row_view'), $completed); + break; + + //- Edit row (=row_edit) + case "row_edit": + $pks = json_decode($_GET['pk']); + $z = 0; + + $tableInfo = $db->getTableInfo($target_table); - for($i=0; $i
"; + + for($i=0; $iquote_id($tableInfo[$j]['name']).' AS \'blob\' FROM '.$db->quote_id($target_table).' WHERE '.$db->wherePK($target_table, json_decode($pks[$i])); + $bl = $db->select($select); + $blobFiles[$j] = $bl['blob']; + unset($bl); + } + else + { + if($_FILES[$i.":".$j]["error"] == UPLOAD_ERR_OK && is_file($_FILES[$i.":".$j]["tmp_name"])) + $blobFiles[$j] = file_get_contents($_FILES[$i.":".$j]["tmp_name"]); + else + $blobFiles[$j] = null; + } + } + else + $value = $_POST[$j][$i]; + } + else + $value = ""; + if(!preg_match('/^BLOB/', $type) && $value===$tableInfo[$j]['dflt_value']) + { + // if the value is the default value, skip it + continue; + } + $all_default = false; + $query_cols .= $db->quote_id($tableInfo[$j]['name']).","; + + $function = $_POST["function_".$j][$i]; + if($function!="") + $query_vals .= $function."("; + + if(preg_match('/^BLOB/', $type)) + $query_vals .= ':blobval'.$j; + elseif(($typeAffinity=="TEXT" || $typeAffinity=="NONE") && !$null) + $query_vals .= $db->quote($value); + elseif(($typeAffinity=="INTEGER" || $typeAffinity=="REAL"|| $typeAffinity=="NUMERIC") && $value=="") + $query_vals .= "NULL"; + elseif($null) + $query_vals .= "NULL"; + else + $query_vals .= $db->quote($value); + if($function!="") + $query_vals .= ")"; + $query_vals .= ","; } - else - $value = ""; - if($value===$result[$j]['dflt_value']) + $query = "INSERT INTO ".$db->quote_id($target_table); + if(!$all_default) { - // if the value is the default value, skip it - continue; - } else - $all_default = false; - $query_cols .= $db->quote_id($fields[$j]).","; - - $type = $result[$j]['type']; - $typeAffinity = get_type_affinity($type); - $function = $_POST["function_".$j][$i]; - if($function!="") - $query_vals .= $function."("; - if(($typeAffinity=="TEXT" || $typeAffinity=="NONE") && !$null) - $query_vals .= $db->quote($value); - elseif(($typeAffinity=="INTEGER" || $typeAffinity=="REAL"|| $typeAffinity=="NUMERIC") && $value=="") - $query_vals .= "NULL"; - elseif($null) - $query_vals .= "NULL"; + $query_cols = substr($query_cols, 0, strlen($query_cols)-1); + $query_vals = substr($query_vals, 0, strlen($query_vals)-1); + + $query.=" (". $query_cols . ") VALUES (". $query_vals. ")"; + } else { + $query .= " DEFAULT VALUES"; + } + + if(isset($blobFiles)) + { + // blob files need to be done using a prepared statement because the query size would be too large + $handle = $db->prepareQuery($query); + foreach($blobFiles as $j=>$blobval) + $db->bindValue($handle, ':blobval'.$j, $blobval, 'blob'); + + $result1 = $db->executePrepared($handle, false); + } else - $query_vals .= $db->quote($value); - if($function!="") - $query_vals .= ")"; - $query_vals .= ","; + $result1 = $db->query($query); + if($result1===false) + $error = true; + $z++; } - $query = "INSERT INTO ".$db->quote_id($target_table); - if(!$all_default) + else { - $query_cols = substr($query_cols, 0, strlen($query_cols)-1); - $query_vals = substr($query_vals, 0, strlen($query_vals)-1); - - $query.=" (". $query_cols . ") VALUES (". $query_vals. ")"; - } else { - $query .= " DEFAULT VALUES"; + $query = "UPDATE ".$db->quote_id($target_table)." SET "; + for($j=0; $jquote_id($tableInfo[$j]['name'])."="; + if($function!="") + $query .= $function."("; + if($null) + $query .= "NULL"; + else + { + if(preg_match('/^BLOB/', $type)) + $query .= ':blobval'.$j; + else + $query .= $db->quote($_POST[$j][$i]); + } + if($function!="") + $query .= ")"; + $query .= ", "; + } + $query = substr($query, 0, -2); + $query .= " WHERE ".$db->wherePK($target_table, json_decode($pks[$i])); + if(isset($blobFiles)) + { + // blob files need to be done using a prepared statement because the query size would be too large + $handle = $db->prepareQuery($query); + foreach($blobFiles as $j=>$filename) + $db->bindValue($handle, ':blobval'.$j, file_get_contents($filename), 'blob'); + + $result1 = $db->executePrepared($handle, false); + } + else + $result1 = $db->query($query); + if($result1===false) + { + $error = true; + } } - $result1 = $db->query($query); - if($result1===false) - $error = true; - $z++; + $completed .= "".htmlencode($query)."
"; } + if($error) + $completed = $db->getError(true); + elseif(isset($_POST['new_row'])) + $completed = $z." ".$lang['rows']." ".$lang['inserted'].".

".$completed; + $params->redirect(array('action'=>'row_view'), $completed); + break; + + + case "row_get_blob": + $blobVal = $db->select("SELECT ".$db->quote_id($_GET['column'])." AS 'blob' FROM ".$db->quote_id($target_table)." WHERE ".$db->wherePK($target_table, json_decode($_GET['pk']))); + $filename = 'download'; + if(function_exists('getimagesizefromstring')) // introduced in PHP 5.4.0 + $imagesize = getimagesizefromstring($blobVal['blob']); + if(isset($imagesize) && $imagesize!==false && isset($imagesize['mime'])) + $mimetype = $imagesize['mime']; + elseif(class_exists('finfo')) // included since php 5.3.0, but might be disabled on Windows + { + $finfo = new finfo(FILEINFO_MIME); + $mimetype = $finfo->buffer($blobVal['blob']); + } + else + $mimetype = "application/octet-stream"; + + if(isset($imagesize) && $imagesize!==false && isset($imagesize[2])) + $extension = image_type_to_extension($imagesize[2]); else + $extension = '.blob'; + ob_end_clean(); + header('Content-Length: '.strlen($blobVal['blob'])); + header("Content-type: ".$mimetype); + if(isset($_GET['download_blob']) && $_GET['download_blob']) + header('Content-Disposition: attachment; filename="'.$filename.$extension.'";'); + header("Pragma: no-cache"); + header("Expires: 0"); + echo $blobVal['blob']; + exit; + break; + + + //- Column actions + + //- Create column (=column_create) + case "column_create": + $num = intval($_POST['rows']); + for($i=0; $i<$num; $i++) { - $query = "UPDATE ".$db->quote_id($target_table)." SET "; - for($j=0; $jquote_id($fields[$j])."="; - if($function!="") - $query .= $function."("; - if($null) - $query .= "NULL"; + $query = "ALTER TABLE ".$db->quote_id($target_table)." ADD ".$db->quote($_POST[$i.'_field'])." "; + $query .= $_POST[$i.'_type']." "; + if(isset($_POST[$i.'_primarykey'])) + $query .= "PRIMARY KEY "; + if(isset($_POST[$i.'_notnull'])) + $query .= "NOT NULL "; + if($_POST[$i.'_defaultoption']!='defined' && $_POST[$i.'_defaultoption']!='none' && $_POST[$i.'_defaultoption']!='expr') + $query .= "DEFAULT ".$_POST[$i.'_defaultoption']." "; + elseif($_POST[$i.'_defaultoption']=='expr') + $query .= "DEFAULT (".$_POST[$i.'_defaultvalue'].") "; + elseif(isset($_POST[$i.'_defaultvalue']) && $_POST[$i.'_defaultoption']=='defined') + { + $typeAffinity = get_type_affinity($_POST[$i.'_type']); + if(($typeAffinity=="INTEGER" || $typeAffinity=="REAL" || $typeAffinity=="NUMERIC") && is_numeric($_POST[$i.'_defaultvalue'])) + $query .= "DEFAULT ".$_POST[$i.'_defaultvalue']." "; + else + $query .= "DEFAULT ".$db->quote($_POST[$i.'_defaultvalue'])." "; + } + if($db->getVersion()==3 && + ($_POST[$i.'_defaultoption']=='defined' || $_POST[$i.'_defaultoption']=='none' || $_POST[$i.'_defaultoption']=='NULL') + // Sqlite3 cannot add columns with default values that are not constant + && !isset($_POST[$i.'_primarykey']) + // sqlite3 cannot add primary key columns + && (!isset($_POST[$i.'_notnull']) || $_POST[$i.'_defaultoption']!='none') + // SQLite3 cannot add NOT NULL columns without DEFAULT even if the table is empty + ) + // use SQLITE3 ALTER TABLE ADD COLUMN + $result = $db->query($query, true); else - $query .= $db->quote($_POST[$j][$i]); - if($function!="") - $query .= ")"; - $query .= ", "; - } - $query = substr($query, 0, sizeof($query)-3); - $query .= " WHERE ".$db->wherePK($target_table, json_decode($pks[$i])); - $result1 = $db->query($query); - if($result1===false) - { - $error = true; + // use ALTER TABLE workaround + $result = $db->query($query, false); + if($result===false) + $error = true; } } - $completed .= "".htmlencode($query)."
"; - } - if(isset($_POST['new_row'])) - $completed = $z." ".$lang['rows']." ".$lang['inserted'].".

".$completed; - $backlinkParameters = "&action=row_view&table=".urlencode($target_table); - break; + if($error) + $completed = $db->getError(true); + else + $completed = $lang['tbl']." '".htmlencode($target_table)."' ".$lang['altered']."."; + $params->redirect(array('action'=>'column_view'), $completed); + break; + + //- Delete column (=column_delete) + case "column_delete": + $pks = explode(":", $_GET['pk']); + $query = "ALTER TABLE ".$db->quote_id($target_table).' DROP '.$db->quote_id($pks[0]); + for($i=1; $iquote_id($pks[$i]); + } + $result = $db->query($query); + if($result === false) + $completed = $db->getError(true); + else + $completed = $lang['tbl']." '".htmlencode($target_table)."' ".$lang['altered']."."; + $params->redirect(array('action'=>'column_view'), $completed); + break; + + //- Add a primary key (=primarykey_add) + case "primarykey_add": + $pks = explode(":", $_GET['pk']); + $query = "ALTER TABLE ".$db->quote_id($target_table).' ADD PRIMARY KEY ('.$db->quote_id($pks[0]); + for($i=1; $iquote_id($pks[$i]); + } + $query .= ")"; + $result = $db->query($query); + if($result === false) + $completed = $db->getError(true); + else + $completed = $lang['tbl']." '".htmlencode($target_table)."' ".$lang['altered']."."; + $params->redirect(array('action'=>'column_view'), $completed); + break; - //- Column actions + //- Edit column (=column_edit) + case "column_edit": + $query = "ALTER TABLE ".$db->quote_id($target_table).' CHANGE '.$db->quote_id($_POST['oldvalue'])." ".$db->quote($_POST['0_field'])." ".$_POST['0_type']; + $result = $db->query($query); + if($result === false) + $completed = $db->getError(true); + else + $completed = $lang['tbl']." '".htmlencode($target_table)."' ".$lang['altered']."."; + $params->redirect(array('action'=>'column_view'), $completed); + break; - //- Create column (=column_create) - case "column_create": - $num = intval($_POST['rows']); - for($i=0; $i<$num; $i++) - { - if($_POST[$i.'_field']!="") + //- Delete trigger (=trigger_delete) + case "trigger_delete": + $query = "DROP TRIGGER ".$db->quote_id($_GET['pk']); + $result = $db->query($query); + if($result === false) + $completed = $db->getError(true); + else + $completed = $lang['trigger']." '".htmlencode($_GET['pk'])."' ".$lang['deleted'].".
".htmlencode($query).""; + $params->redirect(array('action'=>'column_view'), $completed); + break; + + //- Delete index (=index_delete) + case "index_delete": + $query = "DROP INDEX ".$db->quote_id($_GET['pk']); + $result = $db->query($query); + if($result === false) + $completed = $db->getError(true); + else + $completed = $lang['index']." '".htmlencode($_GET['pk'])."' ".$lang['deleted'].".
".htmlencode($query).""; + $params->redirect(array('action'=>'column_view'), $completed); + break; + + //- Create trigger (=trigger_create) + case "trigger_create": + $str = "CREATE TRIGGER ".$db->quote($_POST['trigger_name']); + if($_POST['beforeafter']!="") + $str .= " ".$_POST['beforeafter']; + $str .= " ".$_POST['event']." ON ".$db->quote_id($target_table); + if(isset($_POST['foreachrow'])) + $str .= " FOR EACH ROW"; + if($_POST['whenexpression']!="") + $str .= " WHEN ".$_POST['whenexpression']; + $str .= " BEGIN"; + $str .= " ".$_POST['triggersteps']; + $str .= " END"; + $query = $str; + $result = $db->query($query); + if($result === false) + $completed = $db->getError(true); + else + $completed = $lang['trigger']." ".$lang['created'].".
".htmlencode($query).""; + $params->redirect(array('action'=>'column_view'), $completed); + break; + + //- Create index (=index_create) + case "index_create": + $num = $_POST['num']; + if($_POST['name']=="") + { + $completed = $lang['blank_index']; + } + else if($_POST['0_field']=="") + { + $completed = $lang['one_index']; + } + else { - $query = "ALTER TABLE ".$db->quote_id($target_table)." ADD ".$db->quote($_POST[$i.'_field'])." "; - $query .= $_POST[$i.'_type']." "; - if(isset($_POST[$i.'_primarykey'])) - $query .= "PRIMARY KEY "; - if(isset($_POST[$i.'_notnull'])) - $query .= "NOT NULL "; - if($_POST[$i.'_defaultoption']!='defined' && $_POST[$i.'_defaultoption']!='none' && $_POST[$i.'_defaultoption']!='expr') - $query .= "DEFAULT ".$_POST[$i.'_defaultoption']." "; - elseif($_POST[$i.'_defaultoption']=='expr') - $query .= "DEFAULT (".$_POST[$i.'_defaultvalue'].") "; - elseif(isset($_POST[$i.'_defaultvalue']) && $_POST[$i.'_defaultoption']=='defined') + $str = "CREATE "; + if($_POST['duplicate']=="no") + $str .= "UNIQUE "; + $str .= "INDEX ".$db->quote($_POST['name'])." ON ".$db->quote_id($target_table)." ("; + $str .= $db->quote_id($_POST['0_field']).$_POST['0_order']; + for($i=1; $i<$num; $i++) { - $typeAffinity = get_type_affinity($_POST[$i.'_type']); - if(($typeAffinity=="INTEGER" || $typeAffinity=="REAL" || $typeAffinity=="NUMERIC") && is_numeric($_POST[$i.'_defaultvalue'])) - $query .= "DEFAULT ".$_POST[$i.'_defaultvalue']." "; - else - $query .= "DEFAULT ".$db->quote($_POST[$i.'_defaultvalue'])." "; + if($_POST[$i.'_field']!="") + $str .= ", ".$db->quote_id($_POST[$i.'_field']).$_POST[$i.'_order']; } - if($db->getVersion()==3 && - ($_POST[$i.'_defaultoption']=='defined' || $_POST[$i.'_defaultoption']=='none' || $_POST[$i.'_defaultoption']=='NULL') - // Sqlite3 cannot add columns with default values that are not constant - && !isset($_POST[$i.'_primarykey']) - // sqlite3 cannot add primary key columns - && (!isset($_POST[$i.'_notnull']) || $_POST[$i.'_defaultoption']!='none') - // SQLite3 cannot add NOT NULL columns without DEFAULT even if the table is empty - ) - // use SQLITE3 ALTER TABLE ADD COLUMN - $result = $db->query($query, true); + $str .= ")"; + if(isset($_POST['where']) && $_POST['where']!='') + $str.=" WHERE ".$_POST['where']; + $query = $str; + $result = $db->query($query); + if($result === false) + $completed = $db->getError(true); else - // use ALTER TABLE workaround - $result = $db->query($query, false); - if($result===false) - $error = true; + $completed = $lang['index']." ".$lang['created'].".
".htmlencode($query).""; } - } - $completed = $lang['tbl']." '".htmlencode($target_table)."' ".$lang['altered']."."; - $backlinkParameters = "&action=column_view&table=".urlencode($target_table); - break; - - //- Delete column (=column_delete) - case "column_delete": - $pks = explode(":", $_GET['pk']); - $query = "ALTER TABLE ".$db->quote_id($target_table).' DROP '.$db->quote_id($pks[0]); - for($i=1; $iquote_id($pks[$i]); - } - $result = $db->query($query); - if($result===false) - $error = true; - $completed = $lang['tbl']." '".htmlencode($target_table)."' ".$lang['altered']."."; - $backlinkParameters = "&action=column_view&table=".urlencode($target_table); - break; + $params->redirect(array('action'=>'column_view'), $completed); + break; + } + } +} - //- Add a primary key (=primarykey_add) - case "primarykey_add": - $pks = explode(":", $_GET['pk']); - $query = "ALTER TABLE ".$db->quote_id($target_table).' ADD PRIMARY KEY ('.$db->quote_id($pks[0]); - for($i=1; $iquote_id($pks[$i]); - } - $query .= ")"; - $result = $db->query($query); - if($result===false) - $error = true; - $completed = $lang['tbl']." '".htmlencode($target_table)."' ".$lang['altered']."."; - $backlinkParameters = "&action=column_view&table=".urlencode($target_table); - break; +// if not in debug mode, destroy all output until here +if($debug) + $bufferedOutput = ob_get_contents(); +ob_end_clean(); - //- Edit column (=column_edit) - case "column_edit": - $query = "ALTER TABLE ".$db->quote_id($target_table).' CHANGE '.$db->quote_id($_POST['oldvalue'])." ".$db->quote($_POST['0_field'])." ".$_POST['0_type']; - $result = $db->query($query); - if($result===false) - $error = true; - $completed = $lang['tbl']." '".htmlencode($target_table)."' ".$lang['altered']."."; - $backlinkParameters = "&action=column_view&table=".urlencode($target_table); - break; +//- HTML: output starts here +header('Content-Type: text/html; charset=utf-8'); +?> + + + + + + +<?php echo PROJECT ?> - //- Delete trigger (=trigger_delete) - case "trigger_delete": - $query = "DROP TRIGGER ".$db->quote_id($_GET['pk']); - $result = $db->query($query); - if($result===false) - $error = true; - $completed = $lang['trigger']." '".htmlencode($_GET['pk'])."' ".$lang['deleted'].".
".htmlencode($query).""; - $backlinkParameters = "&action=column_view&table=".urlencode($target_table); - break; +quote_id($_GET['pk']); - $result = $db->query($query); - if($result===false) - $error = true; - $completed = $lang['index']." '".htmlencode($_GET['pk'])."' ".$lang['deleted'].".
".htmlencode($query).""; - $backlinkParameters = "&action=column_view&table=".urlencode($target_table); - break; +// allow themes to be dropped in subfolder "themes" +if(is_file('themes/'.$theme)) $theme = 'themes/'.$theme; - //- Create trigger (=trigger_create) - case "trigger_create": - $str = "CREATE TRIGGER ".$db->quote($_POST['trigger_name']); - if($_POST['beforeafter']!="") - $str .= " ".$_POST['beforeafter']; - $str .= " ".$_POST['event']." ON ".$db->quote_id($target_table); - if(isset($_POST['foreachrow'])) - $str .= " FOR EACH ROW"; - if($_POST['whenexpression']!="") - $str .= " WHEN ".$_POST['whenexpression']; - $str .= " BEGIN"; - $str .= " ".$_POST['triggersteps']; - $str .= " END"; - $query = $str; - $result = $db->query($query); - if($result===false) - $error = true; - $completed = $lang['trigger']." ".$lang['created'].".
".htmlencode($query).""; - $backlinkParameters = "&action=column_view&table=".urlencode($target_table); - break; +if (file_exists($theme)) + // an external stylesheet exists - import it + echo "", PHP_EOL; +else + // only use the default stylesheet if an external one does not exist + echo "", PHP_EOL; - //- Create index (=index_create) - case "index_create": - $num = $_POST['num']; - if($_POST['name']=="") - { - $completed = $lang['blank_index']; - } - else if($_POST['0_field']=="") - { - $completed = $lang['one_index']; - } - else - { - $str = "CREATE "; - if($_POST['duplicate']=="no") - $str .= "UNIQUE "; - $str .= "INDEX ".$db->quote($_POST['name'])." ON ".$db->quote_id($target_table)." ("; - $str .= $db->quote_id($_POST['0_field']).$_POST['0_order']; - for($i=1; $i<$num; $i++) - { - if($_POST[$i.'_field']!="") - $str .= ", ".$db->quote_id($_POST[$i.'_field']).$_POST[$i.'_order']; - } - $str .= ")"; - if(isset($_POST['where']) && $_POST['where']!='') - $str.=" WHERE ".$_POST['where']; - $query = $str; - $result = $db->query($query); - if($result===false) - $error = true; - $completed = $lang['index']." ".$lang['created'].".
".htmlencode($query).""; - } - $backlinkParameters = "&action=column_view&table=".urlencode($target_table); - break; +// HTML: output help text, then exit +if(isset($_GET['help'])) +{ + //help section array + $help = array($lang['help1'] => sprintf($lang['help1_x'], PROJECT, PROJECT, PROJECT)); + for($i=2; isset($lang['help'.$i]); $i++) + $help[$lang['help'.$i]]=$lang['help'.$i.'_x']; + ?> + + +
+ "; + echo "".PROJECT." v".VERSION." ".$lang['help_doc']."

"; + foreach((array)$help as $key => $val) + { + echo "".$key."
"; } + echo "
"; + echo "

"; + foreach((array)$help as $key => $val) + { + echo "
"; + echo "".$key.""; + echo "
"; + echo $val; + echo "
"; + echo "".$lang['back_top'].""; + echo "
"; + } + ?> + + + + isAuthorized()) +{ + //- Javascript include + ?> + + + + + + + + + + + + + + +".$lang['bad_php_directive'].""; + echo ""; + exit(); +} + +//- HTML: login screen if not authorized, exit +if(!$auth->isAuthorized()) +{ + echo "
"; + echo "

v".VERSION."

"; + echo "
"; + if (isset($_GET['failed'])) + echo "".$lang['passwd_incorrect']."

"; + echo $params->getForm(); + echo $lang['passwd'].":
"; + echo "

"; + echo ""; + echo ""; + echo ""; + echo "
"; + echo "
"; + echo "
"; + echo "
"; + echo "".$lang['powered']." ".PROJECT." | "; + printf($lang['page_gen'], $pageTimer); + echo "
"; + echo ""; + exit(); } -// are we working on a view? let's check once here -$target_table_type = $target_table ? $db->getTypeOfTable($target_table) : null; +//- User is authorized, display the main application + +if(count($databases)==0) // the database array is empty, offer to create a new database +{ + //- HTML: form to create a new database, exit + if($directory!==false && is_writable($directory)) + { + echo "
"; + printf($lang['no_db'], PROJECT, PROJECT); + echo "
"; + //if the user has performed some action, show the resulting message + if(isset($_GET['message']) && isset($_SESSION[COOKIENAME.'messages'][$_GET['message']])) + { + echo "
"; + echo $_SESSION[COOKIENAME.'messages'][$_GET['message']]; + echo "

"; + unset($_SESSION[COOKIENAME.'messages'][$_GET['message']]); + } + echo "
".$lang['db_create'].""; + echo $params->getForm(array('table'=>null), 'post', false, 'create_database'); + echo " "; + if(class_exists('SQLiteDatabase') && (class_exists('SQLite3') || class_exists('PDO'))) + { + echo ""; + } + echo ""; + echo ""; + echo "
"; + } + elseif(($directory!==false && !is_executable($directory))) + { + echo "
"; + echo $lang['err'].": ".sprintf($lang['dir_not_executable'], PROJECT, $directory); + echo "

"; + } + else + { + echo "
"; + echo $lang['err'].": ".sprintf($lang['no_db2'], PROJECT); + echo "

"; + } + exit(); +} //- HTML: sidebar echo '"; echo ""; - for($i=0; $i"; + if($tableInfo[$i]['dflt_value'] === "NULL") + $value = NULL; + else + $value = htmlencode(trim(trim($tableInfo[$i]['dflt_value']), "'")); $tdWithClassLeft = ""; echo $tdWithClassLeft; @@ -2941,35 +3049,32 @@ function drawChart() echo htmlencode($type); echo ""; echo $tdWithClassLeft; - echo ""; echo ""; foreach (array_merge($sqlite_functions, $custom_functions) as $f) { echo ""; } echo ""; echo ""; - //we need to have a column dedicated to nulls -di echo $tdWithClassLeft; - if($result[$i]['notnull']==0) + if($tableInfo[$i]['notnull']==0) { - if($result[$i]['dflt_value']==="NULL") - echo ""; + if($value===NULL) + echo ""; else - echo ""; + echo ""; } echo ""; echo $tdWithClassLeft; - if($result[$i]['dflt_value'] === "NULL") - $dflt_value = ""; - else - $dflt_value = htmlencode(deQuoteSQL($result[$i]['dflt_value'])); - + if($typeAffinity=="INTEGER" || $typeAffinity=="REAL" || $typeAffinity=="NUMERIC") - echo ""; + echo ""; + elseif(preg_match('/^BLOB/', $type)) + echo ""; else - echo ""; - echo ""; - echo ""; + echo ""; + echo ""; + echo ""; } echo ""; echo ""; echo "
'; echo "
"; -echo "

"; +echo "

"; echo " v".VERSION.""; echo "

"; echo ""; //- HTML: database list $db->print_db_list(); echo "
"; -echo "null))."'"; if (!$target_table) echo " class='active_table'"; $name = $currentDB['name']; if(strlen($name)>25) - $name = "...".substr($name, strlen($name)-22, 22); + $name = "...".substr($name, strlen($name)-22, 22); echo ">".htmlencode($name).""; echo ""; //- HTML: table list -$query = "SELECT type, name FROM sqlite_master WHERE type='table' OR type='view' ORDER BY name"; -$result = $db->selectArray($query); -$j=0; -for($i=0; $igetTables(true, false); +foreach($tables as $tableName => $tableType) { - if(substr($result[$i]['name'], 0, 7)!="sqlite_" && $result[$i]['name']!="") - { - echo "[".$lang[$result[$i]['type']=='table'?'tbl':'view']."] "; - echo "".htmlencode($result[$i]['name'])."
"; - $j++; - } + echo ""; + echo $params->getLink(array('action'=>'column_view', 'table'=>$tableName), "[".$lang[$tableType=='table'?'tbl':'view']."]"); + echo " "; + echo $params->getLink(array('action'=>'row_view', 'table'=>$tableName), htmlencode($tableName), + ($target_table == $tableName ? 'active_table' : '') ); + echo "
"; } -if($j==0) +if(count($tables)==0) echo $lang['no_tbl']; echo "
"; //- HTML: form to create a new database if($directory!==false && is_writable($directory)) { - echo "
".$lang['db_create']." ".helpLink($lang['help2']).""; - echo "
"; - echo $token_html; + echo "
".$lang['db_create']." ".helpLink($lang['help2']).""; + echo $params->getForm(array('table'=>null), 'post', false, 'create_database'); echo ""; if(class_exists('SQLiteDatabase') && (class_exists('SQLite3') || class_exists('PDO'))) { @@ -1717,8 +2034,7 @@ function get_type_affinity($type) } echo "
"; -echo ""; -echo $token_html; +echo $params->getForm(array(),'get'); echo ""; echo ""; echo "
"; @@ -1726,162 +2042,123 @@ function get_type_affinity($type) echo '

'; //- HTML: breadcrumb navigation -echo "".htmlencode($currentDB['name']).""; +echo $params->getLink(array('table'=>null), htmlencode($currentDB['name'])); if ($target_table) - echo " → ".htmlencode($target_table).""; + echo " → ".$params->getLink(array('action'=>'row_view'), htmlencode($target_table)); echo "

"; -//- HTML: confirmation panel -//if the user has performed some action, show the resulting message -if(isset($_GET['confirm'])) -{ - echo "
"; - echo "
"; - if(isset($error) && $error) //an error occured during the action, so show an error message - echo $lang['err'].": ".htmlencode($db->getError())."
".$lang['bug_report'].' '.PROJECT_BUGTRACKER_LINK; - else //action was performed successfully - show success message - echo $completed; - echo "
"; - if($_GET['action']=="row_delete" || $_GET['action']=="row_create" || $_GET['action']=="row_edit") - echo "

".$lang['return'].""; - else if($_GET['action']=="column_create" || $_GET['action']=="column_delete" || $_GET['action']=="column_edit" || $_GET['action']=="index_create" || $_GET['action']=="index_delete" || $_GET['action']=="trigger_delete" || $_GET['action']=="trigger_create") - echo "

".$lang['return'].""; - else - echo "

".$lang['return'].""; - echo "
"; -} - //- Show the various tab views for a table -if(!isset($_GET['confirm']) && $target_table && isset($_GET['action']) && ($_GET['action']=="table_export" || $_GET['action']=="table_import" || $_GET['action']=="table_sql" || $_GET['action']=="row_view" || $_GET['action']=="row_create" || $_GET['action']=="column_view" || $_GET['action']=="table_rename" || $_GET['action']=="table_search" || $_GET['action']=="table_triggers")) +if($target_table) { - //- HTML: tabs for tables - if($target_table_type == 'table') + //- HTML: tabs + echo $params->getLink(array('action'=>'row_view'), $lang['browse'], + (in_array($_GET['action'], array('row_view', 'row_editordelete') ) ? 'tab_pressed' : 'tab')); + + echo $params->getLink(array('action'=>'column_view'), $lang['struct'], + (in_array($_GET['action'], array('column_view', 'column_edit', 'column_confirm', 'primarykey_add', 'column_create', 'index_create', 'index_delete', 'trigger_create', 'trigger_delete') ) ? 'tab_pressed' : 'tab')); + + echo $params->getLink(array('action'=>'table_sql'), $lang['sql'], + ($_GET['action']=="table_sql" ? 'tab_pressed' : 'tab')); + + echo $params->getLink(array( + 'action' => 'table_search', + 'oldSearch' => (isset($_GET['search'])?$_GET['search']:null) + ), $lang['srch'], ($_GET['action']=="table_search" ? 'tab_pressed' : 'tab')); + + if($target_table_type == 'table' && $db->isWritable() && $db->isDirWritable()) + echo $params->getLink(array('action'=>'row_create'), $lang['insert'], + ($_GET['action']=="row_create" ? 'tab_pressed' : 'tab')); + + echo $params->getLink(array('action'=>'table_export'), $lang['export'], + ($_GET['action']=="table_export" ? 'tab_pressed' : 'tab')); + + if($target_table_type == 'table' && $db->isWritable() && $db->isDirWritable()) + echo $params->getLink(array('action'=>'table_import'), $lang['import'], + ($_GET['action']=="table_import" ? 'tab_pressed' : 'tab')); + + if($db->isWritable() && $db->isDirWritable()) + echo $params->getLink(array('action'=>'table_rename'), $lang['rename'], + ($_GET['action']=="table_rename" ? 'tab_pressed' : 'tab')); + + if($target_table_type == 'table' && $db->isWritable() && $db->isDirWritable()) { - echo "".$lang['browse'].""; - echo "".$lang['struct'].""; - echo "".$lang['sql'].""; - echo "".$lang['srch'].""; - echo "".$lang['insert'].""; - echo "".$lang['export'].""; - echo "".$lang['import'].""; - echo "".$lang['rename'].""; - echo "".$lang['empty'].""; - echo "".$lang['drop'].""; - echo "
"; + echo $params->getLink(array('action'=>'table_empty'), $lang['empty'], + ($_GET['action']=="table_empty" ? 'tab_pressed empty' : 'tab empty')); + + echo $params->getLink(array('action'=>'table_drop'), $lang['drop'], + ($_GET['action']=="table_drop" ? 'tab_pressed drop' : 'tab drop')); + } elseif($db->isWritable() && $db->isDirWritable()) { + echo $params->getLink(array('action'=>'view_drop'), $lang['drop'], + ($_GET['action']=="view_drop" ? 'tab_pressed drop' : 'tab drop')); } - else - //- HTML: tabs for views - { - echo "".$lang['browse'].""; - echo "".$lang['struct'].""; - echo "".$lang['sql'].""; - echo "".$lang['srch'].""; - echo "".$lang['export'].""; - echo "".$lang['drop'].""; - echo "
"; +} +else +//- Show the various tab views for a database +{ + $view = isset($_GET['view']) ? $_GET['view'] : 'structure'; + + echo $params->getLink(array('view'=>'structure'), $lang['struct'], ($view=="structure" ? 'tab_pressed': 'tab') ); + + echo $params->getLink(array('view'=>'sql'), $lang['sql'], ($view=="sql" ? 'tab_pressed': 'tab') ); + + echo $params->getLink(array('view'=>'export'), $lang['export'], ($view=="export" ? 'tab_pressed': 'tab') ); + + if($db->isWritable() && $db->isDirWritable()) + echo $params->getLink(array('view'=>'import'), $lang['import'], ($view=="import" ? 'tab_pressed': 'tab') ); + + if($db->isWritable() && $db->isDirWritable()) + echo $params->getLink(array('view'=>'vacuum'), $lang['vac'], ($view=="vacuum" ? 'tab_pressed': 'tab') ); + + if($directory!==false && is_writable($directory)) + { + + echo $params->getLink(array('view'=>'rename'), $lang['db_rename'], ($view=="rename" ? 'tab_pressed': 'tab') ); + + echo $params->getLink(array('view'=>'delete'), "".$lang['db_del']."", ($view=="delete" ? 'tab_pressed delete_db': 'tab delete_db') ); } } +echo "
"; +echo "
"; + +//- HTML: confirmation panel +//if the user has performed some action, show the resulting message +if(isset($_GET['message']) && isset($_SESSION[COOKIENAME.'messages'][$_GET['message']])) +{ + echo "
"; + echo $_SESSION[COOKIENAME.'messages'][$_GET['message']]; + echo "

"; + unset($_SESSION[COOKIENAME.'messages'][$_GET['message']]); +} + + //- Switch on $_GET['action'] for operations with output if(isset($_GET['action']) && !isset($_GET['confirm'])) { - echo "
"; switch($_GET['action']) { //- Table actions //- Create table (=table_create) case "table_create": - $query = "SELECT name FROM sqlite_master WHERE type='table' AND name=".$db->quote($_POST['tablename']); + $query = "SELECT name FROM sqlite_master WHERE type='table' AND name=".$db->quote($_GET['tablename']); $results = $db->selectArray($query); if(sizeof($results)>0) $exists = true; else $exists = false; - echo "

".$lang['create_tbl'].": '".htmlencode($_POST['tablename'])."'

"; - if($_POST['tablefields']=="" || intval($_POST['tablefields'])<=0) + echo "

".$lang['create_tbl'].": '".htmlencode($_GET['tablename'])."'

"; + if($_GET['tablefields']=="" || intval($_GET['tablefields'])<=0) echo $lang['specify_fields']; - else if($_POST['tablename']=="") + else if($_GET['tablename']=="") echo $lang['specify_tbl']; else if($exists) echo $lang['tbl_exists']; else { - $num = intval($_POST['tablefields']); - $name = $_POST['tablename']; - echo "
"; - echo $token_html; + $num = intval($_GET['tablefields']); + $name = $_GET['tablename']; + echo $params->getForm(array('action'=>'table_create', 'confirm'=>'1')); echo ""; echo ""; echo ""; @@ -1931,7 +2208,7 @@ function get_type_affinity($type) echo ""; echo ""; echo ""; echo "
"; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array(), $lang['cancel']); echo "
"; @@ -1949,11 +2226,11 @@ function get_type_affinity($type) //save the queries in history if necessary if($maxSavedQueries!=0 && $maxSavedQueries!=false) { - if(!isset($_SESSION['query_history'])) - $_SESSION['query_history'] = array(); - $_SESSION['query_history'][md5(strtolower($queryStr))] = $queryStr; - if(sizeof($_SESSION['query_history']) > $maxSavedQueries) - array_shift($_SESSION['query_history']); + if(!isset($_SESSION[COOKIENAME.'query_history'])) + $_SESSION[COOKIENAME.'query_history'] = array(); + $_SESSION[COOKIENAME.'query_history'][md5(strtolower($queryStr))] = $queryStr; + if(sizeof($_SESSION[COOKIENAME.'query_history']) > $maxSavedQueries) + array_shift($_SESSION[COOKIENAME.'query_history']); } $query = explode_sql($delimiter, $queryStr); //explode the query string into individual queries based on the delimiter @@ -1971,9 +2248,10 @@ function get_type_affinity($type) echo "
".$lang['err'].": ".htmlencode($db->getError())."
"; } echo "

"; - if($row = $db->fetch($table_result, 'assoc')) + if($row = $db->fetch($table_result, 'num')) { - $headers = array_keys($row); + for($j=0; $jgetColumnName($table_result,$j); echo ""; echo ""; for($j=0; $j"; $rowCount = 0; - for(; $rowCount==0 || $row = $db->fetch($table_result, 'assoc'); $rowCount++) + for(; $rowCount==0 || $row = $db->fetch($table_result, 'num'); $rowCount++) { $tdWithClass = ""; for($z=0; $zNULL"; else - echo htmlencode(subString($row[$headers[$z]])); + echo htmlencode(subString($row[$z])); echo ""; } echo ""; } $queryTimer->stop(); echo "
"; echo "


"; - - + + if($table_result !== NULL && $table_result !== false) { echo "
"; @@ -2020,7 +2298,7 @@ function get_type_affinity($type) echo "
"; } - + } } } @@ -2033,26 +2311,25 @@ function get_type_affinity($type) echo "
"; echo "".sprintf($lang['run_sql'],htmlencode($db->getName())).""; - echo ""; - echo $token_html; - if(isset($_SESSION['query_history']) && sizeof($_SESSION['query_history'])>0) + echo $params->getForm(array('action'=>'table_sql')); + if(isset($_SESSION[COOKIENAME.'query_history']) && sizeof($_SESSION[COOKIENAME.'query_history'])>0) { echo "".$lang['recent_queries']."

"; } echo "
"; echo ""; + echo ""; echo "
"; echo "
"; echo $lang['fields']."
"; echo ""; echo ""; @@ -2066,58 +2343,54 @@ function get_type_affinity($type) //- Empty table (=table_empty) case "table_empty": - echo ""; - echo $token_html; - echo ""; + echo $params->getForm(array('action'=>'table_empty','confirm'=>'1')); echo "
"; echo sprintf($lang['ques_empty'], htmlencode($target_table))."

"; + echo " ".$lang['vac_on_empty']."

"; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('table'=>null), $lang['cancel']); echo "
"; break; //- Drop table (=table_drop) case "table_drop": - echo ""; - echo $token_html; - echo ""; + echo $params->getForm(array('action'=>'table_drop','confirm'=>'1')); echo "
"; echo sprintf($lang['ques_drop'], htmlencode($target_table))."

"; + echo " ".$lang['vac_on_empty']."

"; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('table'=>null), $lang['cancel']); echo "
"; break; //- Drop view (=view_drop) case "view_drop": - echo ""; - echo $token_html; + echo $params->getForm(array('action'=>'view_drop','confirm'=>'1')); echo ""; echo "
"; echo sprintf($lang['ques_drop_view'], htmlencode($target_table))."

"; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('table'=>null), $lang['cancel']); echo "
"; break; //- Export table (=table_export) case "table_export": - echo ""; - echo $token_html; + echo $params->getForm(); echo "
".$lang['export'].""; echo ""; echo ""; echo "
"; echo "
"; - + echo "
".$lang['options'].""; echo " ".helpLink($lang['help5'])."
"; - echo " ".helpLink($lang['help6'])."
"; - echo " ".helpLink($lang['help7'])."
"; + echo " ".helpLink($lang['help6'])."
"; + echo " ".helpLink($lang['help7'])."
"; echo " ".helpLink($lang['help8'])."
"; - echo " ".helpLink($lang['help9'])."
"; + echo " ".helpLink($lang['help9'])."
"; echo "
"; - + echo ""; - + echo "
"; echo "

"; echo "
".$lang['save_as'].""; @@ -2143,7 +2416,8 @@ function get_type_affinity($type) echo " "; echo "
"; echo ""; - echo "
".sprintf($lang['backup_hint'], "".$lang["backup_hint_linktext"]."")."
"; + echo "
".sprintf($lang['backup_hint'], + $params->getLink(array('download' => $currentDB['path'], 'token' => $_SESSION[COOKIENAME.'token']), $lang["backup_hint_linktext"], '', $lang['backup']))."
"; break; //- Import table (=table_import) @@ -2157,17 +2431,16 @@ function get_type_affinity($type) echo $lang['err'].': '.htmlencode($importSuccess); echo "

"; } - echo "
"; - echo $token_html; + echo $params->getForm(array('action' => 'table_import'), 'post', true); echo "
".$lang['import_into']." ".htmlencode($target_table).""; echo ""; echo "
"; echo "
"; - + echo "
".$lang['options'].""; echo $lang['no_opt']; echo "
"; - + echo ""; - + echo "
"; echo "

"; - + echo "
".$lang['import_f'].""; - echo " "; + echo "".$lang['max_file_size'].": ".number_format(fileUploadMaxSize()/1024/1024)." MiB ".helpLink($lang['help11'])."
"; + echo ""; + echo ""; echo "
"; break; //- Rename table (=table_rename) case "table_rename": - echo ""; - echo $token_html; - echo ""; + echo $params->getForm(array('action'=>'table_rename', 'confirm'=>'1')); printf($lang['rename_tbl'], htmlencode($target_table)); - echo " "; + echo " "; echo "
"; break; //- Search table (=table_search) case "table_search": - $searchValues = array(); - if(isset($_GET['done'])) + if(!isset($_GET['search'])) { - $query = "PRAGMA table_info(".$db->quote_id($target_table).")"; - $result = $db->selectArray($query); - $primary_key = $db->getPrimaryKey($target_table); - $j = 0; - $arr = array(); - for($i=0; $iquote_id($field)." ".$operator; - else{ - if($operator == "LIKE%"){ - $operator = "LIKE"; - if(!preg_match('/(^%)|(%$)/', $value)) $value = '%'.$value.'%'; - $searchValues[$field] = array($value); - $value_quoted = $db->quote($value); - } - elseif($operator == 'IN' || $operator == 'NOT IN') - { - $value = trim($value, '() '); - $values = explode(',',$value); - $values = array_map('trim', $values, array_fill(0,count($values),' \'"')); - if($operator == 'IN') - $searchValues[$field] = $values; - $values = array_map(array($db, 'quote'), $values); - $value_quoted = '(' .implode(', ', $values) . ')'; - } - else - { - $searchValues[$field] = array($value); - $value_quoted = $db->quote($value); - } - $arr[$j] = $db->quote_id($field)." ".$operator." ".$value_quoted; - } - $j++; - } - } - $query = "SELECT *"; - // select the primary key column(s) last (ROWID if there is no PK). - // this will be used to identify rows, e.g. when editing/deleting rows - $primary_key = $db->getPrimaryKey($target_table); - foreach($primary_key as $pk) - { - $query.= ', '.$db->quote_id($pk); - $query.= ', typeof('.$db->quote_id($pk).')'; - } - $query .= " FROM ".$db->quote_id($target_table); - $whereTo = ''; - if(sizeof($arr)>0) - { - $whereTo .= " WHERE ".$arr[0]; - for($i=1; $iquote_id($target_table) . $whereTo; - $queryTimer = new MicroTimer(); - $arr = $db->selectArray($query); - $queryTimer->stop(); - - echo "
"; - echo ""; - if($arr!==false) - { - $affected = sizeof($arr); - echo $lang['showing']." ".$affected." ".$lang['rows'].". "; - printf($lang['query_time'], $queryTimer); - echo "
"; - } - else - { - echo $lang['err'].": ".htmlencode($db->getError()).".
".$lang['bug_report'].' '.PROJECT_BUGTRACKER_LINK.'
'; - } - echo "".htmlencode($query_disp).""; - echo "

"; + $tableInfo = $db->getTableInfo($target_table); - if(sizeof($arr)>0) - { - if($target_table_type == 'view') - { - echo sprintf($lang['readonly_tbl'], htmlencode($target_table))." http://en.wikipedia.org/wiki/View_(database)"; - echo "

"; - } + echo $params->getForm(array('action'=>'table_search', 'confirm'=>'1')); - echo ""; - echo ""; - if($target_table_type == 'table') - { - echo ""; - } - - $header = array(); - for($j=0; $j"; - echo htmlencode($headers[$j]); - echo ""; - } - echo ""; - - $pkFirstCol = sizeof($result)+1; - for($j=0; $j $pk will always be the last columns in each row of the array because we are doing "SELECT *, PK_1, typeof(PK_1), PK2, typeof(PK_2), ... FROM ..." - $pk_arr = array(); - for($col = $pkFirstCol; array_key_exists($col, $arr[$j]); $col=$col+2) - { - // in $col we have the type and in $col-1 the value - if($arr[$j][$col]=='integer' || $arr[$j][$col]=='real') - // json encode as int or float, not string - $pk_arr[] = $arr[$j][$col-1]+0; - else - // encode as json string - $pk_arr[] = $arr[$j][$col-1]; - } - $pk = json_encode($pk_arr); - $tdWithClass = ""; - if($target_table_type == 'table') - { - echo $tdWithClass."".$lang['edit'].""; - echo $tdWithClass."".$lang['del'].""; - } - for($z=0; $z', ''), htmlencode($fldResult)); - echo ""; - } - echo ""; - } - echo "
"; - #todo: make sure the search keywords are kept - #echo ""; - #echo "&".($_SESSION[COOKIENAME.'fulltexts']?'r':'l')."arr; T &".($_SESSION[COOKIENAME.'fulltexts']?'l':'r')."arr;"; - echo "
"; - echo "


"; - } - - echo "".$lang['srch_again'].""; - } - else - { - $query = "PRAGMA table_info(".$db->quote_id($target_table).")"; - $result = $db->selectArray($query); - - echo "
"; - echo $token_html; - echo ""; echo ""; echo ""; @@ -2383,13 +2492,24 @@ function get_type_affinity($type) echo ""; echo ""; - for($i=0; $i"; $tdWithClassLeft = ""; echo $tdWithClassLeft; echo htmlencode($field); @@ -2398,38 +2518,28 @@ function get_type_affinity($type) echo htmlencode($type); echo ""; echo $tdWithClassLeft; - echo ""; + + $operators = array('=', '>', '>=', '<', '<=', "= ''", "!= ''", '!=', 'LIKE', 'LIKE%','NOT LIKE', 'IN', 'NOT IN', 'IS NULL', 'IS NOT NULL'); + $operatorsDisplay = array('LIKE%' => 'LIKE %...%', 'IN'=>'IN (..., ...)', 'NOT IN'=>'NOT IN (..., ...)'); + $operatorsNumbersOnly = array('>', '>=', '<', '<='); + $operatorsTextOnly = array("= ''", "!= ''"); + foreach($operators as $op) { - echo ""; - echo ""; + if($typeAffinity!="INTEGER" && $typeAffinity!="REAL" && $typeAffinity!="NUMERIC" && in_array($op, $operatorsNumbersOnly)) + continue; + if($typeAffinity!="TEXT" && $typeAffinity!="NONE" && in_array($op, $operatorsTextOnly)) + continue; + $display = (isset($operatorsDisplay[$op]) ? $operatorsDisplay[$op] : $op); + echo ""; } - echo ""; - if($typeAffinity=="TEXT" || $typeAffinity=="NONE") - echo ""; - else - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; - echo ""; echo ""; echo ""; echo $tdWithClassLeft; if($typeAffinity=="INTEGER" || $typeAffinity=="REAL" || $typeAffinity=="NUMERIC") - echo ""; + echo ""; else - echo ""; + echo ""; echo ""; echo ""; } @@ -2440,77 +2550,114 @@ function get_type_affinity($type) echo ""; echo "
".$lang['fld']."".$lang['val']."
"; + if(isset($_GET['oldSearch']) && isset($_SESSION[COOKIENAME.'search'][$_GET['oldSearch']]['values'][$field])) + $value = implode($_SESSION[COOKIENAME.'search'][$_GET['oldSearch']]['values'][$field], ","); + else + $value = ''; + if(isset($_GET['oldSearch']) && isset($_SESSION[COOKIENAME.'search'][$_GET['oldSearch']]['operators'][$field])) + $operator = $_SESSION[COOKIENAME.'search'][$_GET['oldSearch']]['operators'][$field]; + elseif($typeAffinity=="TEXT" || $typeAffinity=="NONE") + $operator = 'LIKE'; + else + $operator = '='; + echo "
"; echo "
"; + + break; + } + elseif(isset($_SESSION[COOKIENAME.'search'][$_GET['search']])) + { + $params->search = $_GET['search']; + $search = $_SESSION[COOKIENAME.'search'][$_GET['search']]; + // NOTICE: we do not break here!! we just do the same now like row_view-action does } - break; //- Row actions //- View row (=row_view) case "row_view": - if(!isset($_POST['startRow'])) - $_POST['startRow'] = 0; - - if(isset($_POST['numRows'])) - $_SESSION[COOKIENAME.'numRows'] = intval($_POST['numRows']); - - if(!isset($_SESSION[COOKIENAME.'numRows'])) - $_SESSION[COOKIENAME.'numRows'] = $rowsNum; - - if(isset($_GET['fulltexts'])) - $_SESSION[COOKIENAME.'fulltexts'] = $_GET['fulltexts']; - - if(!isset($_SESSION[COOKIENAME.'fulltexts'])) - $_SESSION[COOKIENAME.'fulltexts'] = false; + if(!isset($_GET['startRow'])) + $_GET['startRow'] = 0; if(isset($_SESSION[COOKIENAME.'currentTable']) && $_SESSION[COOKIENAME.'currentTable']!=$target_table) { unset($_SESSION[COOKIENAME.'sortRows']); - unset($_SESSION[COOKIENAME.'orderRows']); + unset($_SESSION[COOKIENAME.'orderRows']); } - if(isset($_POST['viewtype'])) + if(isset($_GET['viewtype'])) { - $_SESSION[COOKIENAME.'viewtype'] = $_POST['viewtype']; + $_SESSION[COOKIENAME.'viewtype'] = $_GET['viewtype']; } - - $rowCount = $db->numRows($target_table); - $lastPage = intval($rowCount / $_SESSION[COOKIENAME.'numRows']); - $remainder = intval($rowCount % $_SESSION[COOKIENAME.'numRows']); - if($remainder==0) - $remainder = $_SESSION[COOKIENAME.'numRows']; - + + //- Query execution + if(!isset($_GET['sort'])) + $_GET['sort'] = NULL; + if(!isset($_GET['order'])) + $_GET['order'] = NULL; + + $numRows = $params->numRows; + $startRow = $_GET['startRow']; + if(isset($_GET['sort'])) + { + $_SESSION[COOKIENAME.'sortRows'] = $_GET['sort']; + $_SESSION[COOKIENAME.'currentTable'] = $target_table; + } + if(isset($_GET['order'])) + { + $_SESSION[COOKIENAME.'orderRows'] = $_GET['order']; + $_SESSION[COOKIENAME.'currentTable'] = $target_table; + } + $query = "SELECT * "; + // select the primary key column(s) last (ROWID if there is no PK). + // this will be used to identify rows, e.g. when editing/deleting rows + $primary_key = $db->getPrimaryKey($target_table); + foreach($primary_key as $pk) + { + $query.= ', '.$db->quote_id($pk); + $query.= ', typeof('.$db->quote_id($pk).')'; + } + $query .= " FROM ".$db->quote_id($target_table); + $queryDisp = "SELECT * FROM ".$db->quote_id($target_table); + $queryCount = "SELECT COUNT(*) AS count FROM ".$db->quote_id($target_table); + $queryAdd = ""; + if(isset($search) && isset($search['where'])) + { + $queryAdd = $search['where']; + $queryCount .= $search['where']; + } + if(isset($_SESSION[COOKIENAME.'sortRows'])) + $queryAdd .= " ORDER BY ".$db->quote_id($_SESSION[COOKIENAME.'sortRows']); + if(isset($_SESSION[COOKIENAME.'orderRows'])) + $queryAdd .= " ".$_SESSION[COOKIENAME.'orderRows']; + $queryAdd .= " LIMIT ".$startRow.", ".$numRows; + $query .= $queryAdd; + $queryDisp .= $queryAdd; + + $resultRows = $db->select($queryCount); + $totalRows = $resultRows['count']; + $shownRows = min($resultRows['count']-$startRow, $numRows); + //- HTML: pagination buttons + $lastPage = intval($totalRows / $params->numRows); + $remainder = intval($totalRows % $params->numRows); + if($remainder==0) + $remainder = $params->numRows; + echo "
"; //previous button - if($_POST['startRow']>0) + if($_GET['startRow']>0) { echo "
"; - echo "
"; - echo $token_html; + echo $params->getForm(array('action'=>$_GET['action']),'get'); echo ""; - echo " "; - echo " "; + echo " "; echo "
"; echo "
"; echo "
"; - echo "
"; - echo $token_html; - echo ""; - echo " "; - echo " "; + echo $params->getForm(array('action'=>$_GET['action']),'get'); + echo "numRows))."'/>"; + echo " "; echo "
"; echo "
"; } - + //show certain number buttons echo "
"; - echo "
"; - echo $token_html; + echo $params->getForm(array('action'=>$_GET['action'], 'numRows'=>null),'get'); echo " "; - echo " "; + echo " "; echo $lang['rows_records']; - if(intval($_POST['startRow']+$_SESSION[COOKIENAME.'numRows']) < $rowCount) - echo ""; + if(intval($_GET['startRow']+$params->numRows) < $totalRows) + echo "numRows)."'/>"; else echo " "; echo $lang['as_a']; @@ -2528,75 +2675,29 @@ function get_type_affinity($type) echo ""; echo "
"; echo "
"; - + //next button - if(intval($_POST['startRow']+$_SESSION[COOKIENAME.'numRows'])<$rowCount) + if(intval($_GET['startRow']+$params->numRows)<$totalRows) { echo "
"; - echo "
"; - echo $token_html; - echo ""; - echo " "; - echo " "; + echo $params->getForm(array('action'=>$_GET['action']),'get'); + echo "numRows)."'/>"; + echo " "; echo "
"; echo "
"; echo "
"; - echo "
"; - echo $token_html; - echo ""; - echo " "; - echo " "; + echo $params->getForm(array('action'=>$_GET['action']),'get'); + echo ""; + echo " "; echo "
"; echo "
"; } echo "
"; echo "
"; - //- Query execution - if(!isset($_GET['sort'])) - $_GET['sort'] = NULL; - if(!isset($_GET['order'])) - $_GET['order'] = NULL; - - $numRows = $_SESSION[COOKIENAME.'numRows']; - $startRow = $_POST['startRow']; - if(isset($_GET['sort'])) - { - $_SESSION[COOKIENAME.'sortRows'] = $_GET['sort']; - $_SESSION[COOKIENAME.'currentTable'] = $target_table; - } - if(isset($_GET['order'])) - { - $_SESSION[COOKIENAME.'orderRows'] = $_GET['order']; - $_SESSION[COOKIENAME.'currentTable'] = $target_table; - } - $_SESSION[COOKIENAME.'numRows'] = $numRows; - $query = "SELECT * "; - // select the primary key column(s) last (ROWID if there is no PK). - // this will be used to identify rows, e.g. when editing/deleting rows - $primary_key = $db->getPrimaryKey($target_table); - foreach($primary_key as $pk) - { - $query.= ', '.$db->quote_id($pk); - $query.= ', typeof('.$db->quote_id($pk).')'; - } - $query .= " FROM ".$db->quote_id($target_table); - $queryDisp = "SELECT * FROM ".$db->quote_id($target_table); - $queryCount = "SELECT MIN(COUNT(*),".$numRows.") AS count FROM ".$db->quote_id($target_table); - $queryAdd = ""; - if(isset($_SESSION[COOKIENAME.'sortRows'])) - $queryAdd .= " ORDER BY ".$db->quote_id($_SESSION[COOKIENAME.'sortRows']); - if(isset($_SESSION[COOKIENAME.'orderRows'])) - $queryAdd .= " ".$_SESSION[COOKIENAME.'orderRows']; - $queryAdd .= " LIMIT ".$startRow.", ".$numRows; - $query .= $queryAdd; - $queryDisp .= $queryAdd; - - $resultRows = $db->select($queryCount); - $resultRows = $resultRows['count']; //- Show results - if($resultRows>0) + if($shownRows>0) { $queryTimer = new MicroTimer(); $table_result = $db->query($query); @@ -2604,53 +2705,46 @@ function get_type_affinity($type) echo "
"; - echo "".$lang['showing_rows']." ".$startRow." - ".($startRow + $resultRows-1).", ".$lang['total'].": ".$rowCount." "; + echo "".$lang['showing_rows']." ".$startRow." - ".($startRow + $shownRows-1).", ".$lang['total'].": ".$totalRows." "; printf($lang['query_time'], $queryTimer); echo "
"; echo "".htmlencode($queryDisp).""; echo "

"; - + if($target_table_type == 'view') { - echo sprintf($lang['readonly_tbl'], htmlencode($target_table))." http://en.wikipedia.org/wiki/View_(database)"; - echo "

"; + echo sprintf($lang['readonly_tbl'], htmlencode($target_table))." https://en.wikipedia.org/wiki/View_(SQL)"; + echo "

"; } - - $query = "PRAGMA table_info(".$db->quote_id($target_table).")"; - $result = $db->selectArray($query); - $pkFirstCol = sizeof($result)+1; + + $tableInfo = $db->getTableInfo($target_table); + $pkFirstCol = sizeof($tableInfo)+1; //- Table view if(!isset($_SESSION[COOKIENAME.'viewtype']) || $_SESSION[COOKIENAME.'viewtype']=="table") { - echo "
"; - echo $token_html; + echo $params->getForm(array('action'=>'row_editordelete'), 'post', false, 'checkForm'); echo ""; echo ""; - if($target_table_type == 'table') - { - echo ""; - } + echo ""; - for($i=0; $i"; - echo "".htmlencode($result[$i]['name']).""; - if(isset($_SESSION[COOKIENAME.'sortRows']) && $_SESSION[COOKIENAME.'sortRows']==$result[$i]['name']) + echo $params->getLink(array('action'=>$_GET['action'], 'sort'=>$tableInfo[$i]['name'], 'order'=>$orderTag ), htmlencode($tableInfo[$i]['name'])); + if(isset($_SESSION[COOKIENAME.'sortRows']) && $_SESSION[COOKIENAME.'sortRows']==$tableInfo[$i]['name']) echo (($_SESSION[COOKIENAME.'orderRows']=="ASC") ? " " : " "); echo ""; } echo ""; - for($i=0; $row = $db->fetch($table_result); $i++) + for($i=0; $row = $db->fetch($table_result, 'num'); $i++) { // -g-> $pk will always be the last columns in each row of the array because we are doing "SELECT *, PK_1, typeof(PK_1), PK2, typeof(PK_2), ... FROM ..." $pk_arr = array(); @@ -2662,28 +2756,30 @@ function get_type_affinity($type) $pk_arr[] = $row[$col-1]+0; else // encode as json string - $pk_arr[] = $row[$col-1]; + $pk_arr[] = $row[$col-1]; } $pk = json_encode($pk_arr); $tdWithClass = ""; - if($target_table_type == 'table') + if($target_table_type == 'table' && $db->isWritable() && $db->isDirWritable()) { echo $tdWithClass; echo ""; echo ""; echo $tdWithClass; // -g-> Here, we need to put the PK in as the link for both the edit and delete. - echo "".$lang['edit'].""; + echo $params->getLink(array('action'=>'row_editordelete', 'pk'=>$pk, 'type'=>'edit'),"".$lang['edit']."",'edit', $lang['edit']); echo ""; echo $tdWithClass; - echo "".$lang['del'].""; + echo $params->getLink(array('action'=>'row_editordelete', 'pk'=>$pk, 'type'=>'delete'),"".$lang['del']."",'delete', $lang['del']); echo ""; + } else { + echo ""; } - for($j=0; $jNULL"; + elseif(preg_match('/^BLOB/i', $tableInfo[$j]['type'])) + { + echo "
"; + echo $params->getLink(array('action'=>'row_get_blob', 'confirm'=>1, 'pk'=>$pk, 'column'=>$tableInfo[$j]['name'], 'download_blob'=>1),$lang["download"]).' | '; + echo $params->getLink(array('action'=>'row_get_blob', 'confirm'=>1, 'pk'=>$pk, 'column'=>$tableInfo[$j]['name'], 'download_blob'=>0),$lang["open_in_browser"],'','','_blank'); + echo "
"; + echo 'Size: '.number_format(strlen($row[$j])).' Bytes'; + echo "
"; + } + elseif(isset($search)) + echo markSearchWords(subString($row[$j]),$tableInfo[$j]['name'], $search); else echo htmlencode(subString($row[$j])); echo ""; @@ -2699,7 +2806,7 @@ function get_type_affinity($type) echo "
"; } echo "
"; - echo ""; - echo "&".($_SESSION[COOKIENAME.'fulltexts']?'r':'l')."arr; T &".($_SESSION[COOKIENAME.'fulltexts']?'l':'r')."arr;"; - echo ""; + echo "$_GET['action'], 'fulltexts'=>($params->fulltexts?0:1) ))."' title='".$lang[($params->fulltexts?'no_full_texts':'full_texts')]."'>"; + echo "&".($params->fulltexts?'r':'l')."arr; T &".($params->fulltexts?'l':'r')."arr;"; + echo "
"; $tdWithClassLeft = ""; echo "
"; - if($target_table_type == 'table') + if($target_table_type == 'table' && $db->isWritable() && $db->isDirWritable()) { echo "".$lang['chk_all']." / ".$lang['unchk_all']." ".$lang['with_sel'].": "; echo ""; echo ""; else echo ""; @@ -2902,15 +3011,13 @@ function drawChart() echo " "; echo "
"; echo "
"; - $query = "PRAGMA table_info(".$db->quote_id($target_table).")"; - $result = $db->selectArray($query); - echo "
"; - echo $token_html; - if(isset($_POST['num'])) - $num = $_POST['num']; + echo $params->getForm(array('action'=>'row_create','confirm'=>'1'), 'post', true); + $tableInfo = $db->getTableInfo($target_table); + if(isset($_GET['newRows'])) + $num = $_GET['newRows']; else $num = 1; - echo ""; + echo ""; for($j=0; $j<$num; $j++) { if($j>0) @@ -2924,14 +3031,15 @@ function drawChart() echo "
".$lang['val']."
"; echo "
"; @@ -2978,8 +3083,6 @@ function drawChart() echo "

"; } - $fieldStr = substr($fieldStr, 1); - echo ""; echo ""; break; @@ -2990,40 +3093,26 @@ function drawChart() else if(isset($_GET['pk'])) $pks = array($_GET['pk']); else $pks[0] = ""; - $str = $pks[0]; - for($i=1; $i"; echo $lang['err'].": ".$lang['no_sel']; echo ""; - echo "

".$lang['return'].""; + echo "

".$params->getLink(array('action'=>'row_view'),$lang['return']); } else { if((isset($_POST['type']) && $_POST['type']=="edit") || (isset($_GET['type']) && $_GET['type']=="edit")) //edit { - echo "
"; - echo $token_html; - $query = "PRAGMA table_info(".$db->quote_id($target_table).")"; - $result = $db->selectArray($query); - - //build the POST array of fields - $fieldStr = $result[0][1]; - for($j=1; $jgetForm(array('action'=>'row_edit', 'confirm'=>'1', 'pk'=>json_encode($pks)),'post',true); + $tableInfo = $db->getTableInfo($target_table); $primary_key = $db->getPrimaryKey($target_table); - - echo ""; for($j=0; $jquote_id($target_table)." WHERE " . $db->wherePK($target_table, json_decode($pks[$j])); - $result1 = $db->select($query); + $result1 = $db->select($query, 'num'); echo ""; echo ""; @@ -3034,23 +3123,22 @@ function drawChart() echo ""; echo ""; - for($i=0; $i"; $tdWithClassLeft = ""; - echo $tdWithClass; + echo $tdWithClassLeft; echo htmlencode($field); echo ""; - echo $tdWithClass; + echo $tdWithClassLeft; echo htmlencode($type); echo ""; echo $tdWithClassLeft; - echo ""; echo ""; foreach (array_merge($sqlite_functions, $custom_functions) as $f) { echo ""; @@ -3058,19 +3146,34 @@ function drawChart() echo ""; echo ""; echo $tdWithClassLeft; - if($result[$i][3]==0) + if($tableInfo[$i]['notnull']==0) { if($value===NULL) - echo ""; + echo ""; else - echo ""; + echo ""; } echo ""; echo $tdWithClassLeft; if($typeAffinity=="INTEGER" || $typeAffinity=="REAL" || $typeAffinity=="NUMERIC") - echo ""; + echo ""; + elseif(preg_match('/^BLOB/', $type)) + { + if($value!==NULL) + { + echo ""; + echo $params->getLink(array('action'=>'row_get_blob', 'confirm'=>1, 'pk'=>$pks[$j], 'column'=>$field, 'download_blob'=>1),$lang["download"]).' | '; + echo $params->getLink(array('action'=>'row_get_blob', 'confirm'=>1, 'pk'=>$pks[$j], 'column'=>$field, 'download_blob'=>0),$lang["open_in_browser"],'','','_blank').'
'; + echo ""; + } + echo ""; + } else - echo ""; + echo ""; echo ""; echo ""; } @@ -3079,7 +3182,7 @@ function drawChart() // Note: the 'Save changes' button must be first in the code so it is the one used when submitting the form with the Enter key (issue #215) echo " "; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('action'=>'row_view'), $lang['cancel']); echo ""; echo ""; echo "
".$lang['val']."
"; echo "
"; @@ -3089,13 +3192,12 @@ function drawChart() } else //delete { - echo ""; - echo $token_html; + echo $params->getForm(array('action'=>'row_delete', 'confirm'=>'1', 'pk'=>json_encode($pks))); echo "
"; printf($lang['ques_del_rows'], htmlencode($str), htmlencode($target_table)); echo "

"; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('action'=>'row_view'), $lang['cancel']); echo "
"; } } @@ -3105,14 +3207,12 @@ function drawChart() //- View table structure (=column_view) case "column_view": - $query = "PRAGMA table_info(".$db->quote_id($target_table).")"; - $result = $db->selectArray($query); + $tableInfo = $db->getTableInfo($target_table); - echo ""; - echo $token_html; + echo $params->getForm(array('action'=>'column_confirm'), 'get', false, 'checkForm'); echo ""; echo ""; - if($target_table_type == 'table') + if($target_table_type == 'table' && $db->isWritable() && $db->isDirWritable()) echo ""; echo ""; echo ""; @@ -3123,15 +3223,15 @@ function drawChart() echo ""; $noPrimaryKey = true; - - for($i=0; $i"; $tdWithClassLeft = ""; - if($target_table_type == 'table') + if($target_table_type == 'table' && $db->isWritable() && $db->isDirWritable()) { echo $tdWithClass; echo ""; echo ""; echo $tdWithClass; - echo "".$lang['edit'].""; + echo $params->getLink(array('action'=>'column_edit', 'pk'=>$fieldVal),"".$lang['edit']."",'edit', $lang['edit']); echo ""; echo $tdWithClass; - echo "".$lang['del'].""; + echo $params->getLink(array('action'=>'column_confirm', 'action2'=>'column_delete', 'pk'=>$fieldVal),"".$lang['del']."",'delete', $lang['del']); echo ""; } echo $tdWithClass; @@ -3187,7 +3287,7 @@ function drawChart() } echo "
".$lang['col']." #".$lang['fld']."
"; echo "
"; - if($target_table_type == 'table') + if($target_table_type == 'table' && $db->isWritable() && $db->isDirWritable()) { echo "".$lang['chk_all']." / ".$lang['unchk_all']." ".$lang['with_sel'].": "; echo ""; } echo ""; - if($target_table_type == 'table') + if($target_table_type == 'table' && $db->isWritable() && $db->isDirWritable()) { echo "
"; - echo "
"; - echo $token_html; - echo ""; + echo $params->getForm(array('action'=>'column_create'), 'get'); echo $lang['add']." ".$lang['tbl_end']." "; echo "
"; } - + $query = "SELECT sql FROM sqlite_master WHERE name=".$db->quote($target_table); $master = $db->selectArray($query); - + echo "
"; echo "
"; echo "
"; @@ -3222,7 +3320,6 @@ function drawChart() if($target_table_type != 'view') { echo "


"; - //$query = "SELECT * FROM sqlite_master WHERE type='index' AND tbl_name='".$target_table."'"; $query = "PRAGMA index_list(".$db->quote_id($target_table).")"; $result = $db->selectArray($query); if(sizeof($result)>0) @@ -3255,7 +3352,7 @@ function drawChart() $tdWithClassLeftSpan = ""; echo ""; echo $tdWithClassSpan; - echo "".$lang['del'].""; + echo $params->getLink(array('action'=>'index_delete', 'pk'=>$result[$i]['name']), "".$lang['del']."", 'delete', $lang['del']); echo ""; echo $tdWithClassLeftSpan; echo $result[$i]['name']; @@ -3281,7 +3378,7 @@ function drawChart() } echo "

"; } - + $query = "SELECT * FROM sqlite_master WHERE type='trigger' AND tbl_name=".$db->quote($target_table)." ORDER BY name"; $result = $db->selectArray($query); //print_r($result); @@ -3300,7 +3397,7 @@ function drawChart() $tdWithClass = ""; echo ""; echo $tdWithClass; - echo "".$lang['del'].""; + echo $params->getLink(array('action'=>'trigger_delete', 'pk'=>$result[$i]['name']), "".$lang['del']."", 'delete', $lang['del']); echo ""; echo $tdWithClass; echo htmlencode($result[$i]['name']); @@ -3311,47 +3408,44 @@ function drawChart() } echo "

"; } - - echo "
"; - echo $token_html; - echo ""; - echo "
"; - echo $lang['create_index2']." ".$lang['cols']." "; - echo "
"; - echo "
"; - - echo "
"; - echo $token_html; - echo ""; - echo "
"; - echo $lang['create_trigger2']." "; - echo "
"; - echo "
"; + + if($db->isWritable() && $db->isDirWritable()) + { + echo $params->getForm(array('action'=>'index_create'),'get'); + echo "
"; + echo $lang['create_index2']." ".$lang['cols']." "; + echo "
"; + echo ""; + + echo $params->getForm(array('action'=>'trigger_create'),'get'); + echo "
"; + echo $lang['create_trigger2']." "; + echo "
"; + echo ""; + } } break; //- Create column (=column_create) case "column_create": - echo "

".sprintf($lang['new_fld'],htmlencode($_POST['tablename']))."

"; - if($_POST['tablefields']=="" || intval($_POST['tablefields'])<=0) + echo "

".sprintf($lang['new_fld'],htmlencode($_GET['table']))."

"; + if($_GET['tablefields']=="" || intval($_GET['tablefields'])<=0) echo $lang['specify_fields']; - else if($_POST['tablename']=="") + else if($_GET['table']=="") echo $lang['specify_tbl']; else { - $num = intval($_POST['tablefields']); - $name = $_POST['tablename']; - echo "
"; - echo $token_html; - echo ""; + $num = intval($_GET['tablefields']); + $name = $_GET['table']; + echo $params->getForm(array('action'=>'column_create', 'confirm'=>'1')); echo ""; echo ""; echo ""; - $headings = array($lang["fld"], $lang["type"], $lang["prim_key"]); + $headings = array($lang["fld"], $lang["type"], $lang["prim_key"]); if($db->getType() != "SQLiteDatabase") $headings[] = $lang["autoincrement"]; $headings[] = $lang["not_null"]; $headings[] = $lang["def_val"]; - + for($k=0; $k" . $headings[$k] . ""; echo ""; @@ -3393,7 +3487,7 @@ function drawChart() echo ""; echo ""; echo ""; echo "
"; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('action'=>'column_view'), $lang['cancel']); echo "
"; @@ -3403,18 +3497,19 @@ function drawChart() //- Delete column (=column_confirm) case "column_confirm": - if(isset($_POST['check'])) - $pks = $_POST['check']; + if(isset($_GET['check'])) + $pks = $_GET['check']; elseif(isset($_GET['pk'])) $pks = array($_GET['pk']); else $pks = array(); - + if(sizeof($pks)==0) //nothing was selected so show an error { echo "
"; echo $lang['err'].": ".$lang['no_sel']; echo "
"; - echo "

".$lang['return'].""; + echo "

"; + echo $params->getLink(array('action'=>'column_view'), $lang['return']); } else { @@ -3425,13 +3520,12 @@ function drawChart() $str .= ", ".$pks[$i]; $pkVal .= ":".$pks[$i]; } - echo ""; - echo $token_html; + echo $params->getForm(array('action'=>$_GET['action2'], 'confirm'=>'1', 'pk'=>$pkVal)); echo "
"; - printf($lang['ques_'.$_REQUEST['action2']], htmlencode($str), htmlencode($target_table)); + printf($lang['ques_'.$_GET['action2']], htmlencode($str), htmlencode($target_table)); echo "

"; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('action'=>'column_view'), $lang['cancel']); echo "
"; } break; @@ -3446,131 +3540,132 @@ function drawChart() echo $lang['specify_tbl']; else { - $query = "PRAGMA table_info(".$db->quote_id($target_table).")"; - $result = $db->selectArray($query); + $tableInfo = $db->getTableInfo($target_table); - for($i=0; $i"; - echo $token_html; - echo ""; - echo ""; - echo ""; - echo ""; - //$headings = array("Field", "Type", "Primary Key", "Autoincrement", "Not NULL", "Default Value"); - $headings = array($lang["fld"], $lang["type"]); - for($k=0; $k".$headings[$k].""; - echo ""; - - $i = 0; - $tdWithClass = ""; - echo $tdWithClass; - echo ""; - echo ""; - echo $tdWithClass; - echo ""; - echo ""; - /* - echo $tdWithClass; - if($primarykeyVal) - echo " Yes"; - else - echo " Yes"; - echo ""; - echo $tdWithClass; - if(1==2) - echo " Yes"; - else - echo " Yes"; - echo ""; - echo $tdWithClass; - if($notnullVal) - echo " Yes"; else - echo " Yes"; - echo ""; - echo $tdWithClass; - echo ""; - echo ""; - */ - echo ""; + { + $name = $target_table; + echo $params->getForm(array('action'=>'column_edit', 'confirm'=>'1')); + echo ""; + echo "
"; - echo "
"; + echo ""; + //$headings = array("Field", "Type", "Primary Key", "Autoincrement", "Not NULL", "Default Value"); + $headings = array($lang["fld"], $lang["type"]); + for($k=0; $k".$headings[$k].""; + echo ""; - echo ""; - echo ""; - echo ""; - echo "
"; - echo " "; - echo "".$lang['cancel'].""; - echo "
"; - echo ""; + $i = 0; + $tdWithClass = ""; + echo ""; + echo $tdWithClass; + echo ""; + echo ""; + echo $tdWithClass; + echo ""; + echo ""; + /* + echo $tdWithClass; + if($primarykeyVal) + echo " Yes"; + else + echo " Yes"; + echo ""; + echo $tdWithClass; + if(1==2) + echo " Yes"; + else + echo " Yes"; + echo ""; + echo $tdWithClass; + if($notnullVal) + echo " Yes"; + else + echo " Yes"; + echo ""; + echo $tdWithClass; + echo ""; + echo ""; + */ + echo ""; + + echo ""; + echo ""; + echo " "; + echo $params->getLink(array('action'=>'column_view'), $lang['cancel']); + echo ""; + echo ""; + echo ""; + echo ""; + } } break; //- Delete index (=index_delete) case "index_delete": - echo "
"; - echo $token_html; + echo $params->getForm(array('action'=>'index_delete', 'pk'=>$_GET['pk'], 'confirm'=>'1')); echo "
"; echo sprintf($lang['ques_del_index'], htmlencode($_GET['pk']))."

"; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('action'=>'column_view'), $lang['cancel']); echo "
"; echo "
"; break; //- Delete trigger (=trigger_delete) case "trigger_delete": - echo "
"; - echo $token_html; + echo $params->getForm(array('action'=>'trigger_delete', 'pk'=>$_GET['pk'], 'confirm'=>'1')); echo "
"; echo sprintf($lang['ques_del_trigger'], htmlencode($_GET['pk']))."

"; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('action'=>'column_view'), $lang['cancel']); echo "
"; echo "
"; break; //- Create trigger (=trigger_create) case "trigger_create": - echo "

".$lang['create_trigger']." '".htmlencode($_POST['tablename'])."'

"; - if($_POST['tablename']=="") + echo "

".$lang['create_trigger']." '".htmlencode($_GET['table'])."'

"; + if($_GET['table']=="") echo $lang['specify_tbl']; else { - echo "
"; - echo $token_html; + echo $params->getForm(array('action'=>'trigger_create', 'confirm'=>'1')); echo $lang['trigger_name'].":

"; echo "
".$lang['db_event'].""; echo $lang['before']."/".$lang['after'].": "; echo ""; echo "

"; echo $lang['event'].": "; @@ -3589,26 +3684,23 @@ function drawChart() echo ""; echo "


"; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('action'=>'column_view'), $lang['cancel']); echo "
"; } break; //- Create index (=index_create) case "index_create": - echo "

".$lang['create_index']." '".htmlencode($_POST['tablename'])."'

"; - if($_POST['numcolumns']=="" || intval($_POST['numcolumns'])<=0) + echo "

".$lang['create_index']." '".htmlencode($_GET['table'])."'

"; + if($_GET['numcolumns']=="" || intval($_GET['numcolumns'])<=0) echo $lang['specify_fields']; - else if($_POST['tablename']=="") + else if($_GET['table']=="") echo $lang['specify_tbl']; else { - echo "
"; - echo $token_html; - $num = intval($_POST['numcolumns']); - $query = "PRAGMA table_info(".$db->quote_id($_POST['tablename']).")"; - - $result = $db->selectArray($query); + echo $params->getForm(array('action'=>'index_create', 'confirm'=>'1')); + $num = intval($_GET['numcolumns']); + $tableInfo = $db->getTableInfo($_GET['table']); echo "
".$lang['define_index'].""; echo "
"; echo ""; @@ -3625,8 +3717,8 @@ function drawChart() { echo " "; echo ""; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array('action'=>'column_view'), $lang['cancel']); echo ""; } break; @@ -3646,292 +3738,192 @@ function drawChart() echo "
"; } -$view = "structure"; - -//- HMTL: tabs for databases +//- HMTL: views for databases if(!$target_table && !isset($_GET['confirm']) && (!isset($_GET['action']) || (isset($_GET['action']) && $_GET['action']!="table_create"))) //the absence of these fields means we are viewing the database homepage { - $view = isset($_GET['view']) ? $_GET['view'] : 'structure'; - - echo "".$lang['struct'].""; - echo "".$lang['sql'].""; - echo "".$lang['export'].""; - echo "".$lang['import'].""; - echo "".$lang['vac'].""; - if($directory!==false && is_writable($directory)) - { - echo "".$lang['db_rename'].""; - - echo "".$lang['db_del'].""; - } - echo "
"; - echo "
"; - //- Switch on $view (actually a series of if-else) if($view=="structure") { //- Database structure, shows all the tables (=structure) - - if(isset($dbexists)) - { - echo "
"; - echo $lang['err'].': '.sprintf($lang['db_exists'], htmlencode($dbname)); - echo "

"; - } - + if($db->isWritable() && !$db->isDirWritable()) { - echo "
"; + echo "
"; echo $lang['attention'].': '.$lang['directory_not_writable']; echo "

"; } - - if(isset($extension_not_allowed)) + elseif(!$db->isWritable()) { - echo "
"; - echo $lang['extension_not_allowed'].': '; - echo implode(', ', array_map('htmlencode', $allowed_extensions)); - echo '
'.$lang['add_allowed_extension']; + echo "
"; + echo $lang['attention'].': '.$lang['database_not_writable']; echo "

"; } if ($auth->isPasswordDefault()) { echo "
"; - echo sprintf($lang['warn_passwd'],(is_readable('phpliteadmin.config.php')?'phpliteadmin.config.php':PAGE))."
".$lang['warn0']; + echo sprintf($lang['warn_passwd'],(is_readable('phpliteadmin.config.php')?'phpliteadmin.config.php':basename(__FILE__)))."
".$lang['warn0']; echo "
"; } - + echo "".$lang['db_name'].": ".htmlencode($db->getName())."
"; echo "".$lang['db_path'].": ".htmlencode($db->getPath())."
"; - echo "".$lang['db_size'].": ".$db->getSize()." KB
"; + echo "".$lang['db_size'].": ".number_format($db->getSize())." KiB
"; echo "".$lang['db_mod'].": ".$db->getDate()."
"; echo "".$lang['sqlite_v'].": ".$db->getSQLiteVersion()."
"; - echo "".$lang['sqlite_ext']." ".helpLink($lang['help1']).": ".$db->getType()."
"; + echo "".$lang['sqlite_ext']." ".helpLink($lang['help1']).": ".$db->getType()."
"; echo "".$lang['php_v'].": ".phpversion()."
"; echo "".PROJECT." ".$lang["ver"].": ".VERSION; echo "

"; echo ""; - + if(isset($_GET['sort']) && ($_GET['sort']=='type' || $_GET['sort']=='name')) $_SESSION[COOKIENAME.'sortTables'] = $_GET['sort']; if(isset($_GET['order']) && ($_GET['order']=='ASC' || $_GET['order']=='DESC')) $_SESSION[COOKIENAME.'orderTables'] = $_GET['order']; - - $query = "SELECT type, name FROM sqlite_master WHERE (type='table' OR type='view') AND name!='' AND name NOT LIKE 'sqlite_%'"; - $queryAdd = ""; - if(isset($_SESSION[COOKIENAME.'sortTables'])) - $queryAdd .= " ORDER BY ".$db->quote_id($_SESSION[COOKIENAME.'sortTables']); - else - $queryAdd .= " ORDER BY \"name\""; - if(isset($_SESSION[COOKIENAME.'orderTables'])) - $queryAdd .= " ".$_SESSION[COOKIENAME.'orderTables']; - $query .= $queryAdd; - $result = $db->selectArray($query); - if(sizeof($result)==0) + if(!isset($_SESSION[COOKIENAME.'sortTables'])) + $_SESSION[COOKIENAME.'sortTables'] = 'name'; + + if(!isset($_SESSION[COOKIENAME.'orderTables'])) + $_SESSION[COOKIENAME.'orderTables'] = 'ASC'; + + $tables = $db->getTables(true, false, $_SESSION[COOKIENAME.'sortTables'], $_SESSION[COOKIENAME.'orderTables']); + + if(sizeof($tables)==0) echo $lang['no_tbl']."

"; else { echo ""; echo ""; - + echo ""; - + echo ""; - + echo ""; echo ""; echo ""; - + $totalRecords = 0; $skippedTables = false; - for($i=0; $i $tableType) { - $records = $db->numRows($result[$i]['name'], (!isset($_GET['forceCount']))); + $records = $db->numRows($tableName, (!isset($_GET['forceCount']))); if($records == '?') { $skippedTables = true; - $records = "?"; + $records = $params->getLink(array('forceCount'=>'1'), '?'); } else $totalRecords += $records; $tdWithClass = ""; - echo $tdWithClassLeft; - echo $lang['tbl']; - echo ""; - echo $tdWithClassLeft; - echo "".htmlencode($result[$i]['name']).""; - echo ""; - echo $tdWithClass; - echo "".$lang['browse'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['struct'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['sql'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['srch'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['insert'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['export'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['import'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['rename'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['empty'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['drop'].""; - echo ""; - echo $tdWithClass; - echo $records; - echo ""; - echo ""; - } - else - { - echo ""; - echo $tdWithClassLeft; - echo $lang['view']; - echo ""; - echo $tdWithClassLeft; - echo "".htmlencode($result[$i]['name']).""; - echo ""; - echo $tdWithClass; - echo "".$lang['browse'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['struct'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['sql'].""; - echo ""; - echo $tdWithClass; - echo "".$lang['srch'].""; - echo ""; - echo $tdWithClass; - echo ""; - echo ""; - echo $tdWithClass; - echo "".$lang['export'].""; - echo ""; - echo $tdWithClass; - echo ""; - echo ""; - echo $tdWithClass; - echo ""; - echo ""; - echo $tdWithClass; - echo ""; - echo ""; - echo $tdWithClass; - echo "".$lang['drop'].""; - echo ""; - echo $tdWithClass; - echo $records; - echo ""; - echo ""; - } + + echo ""; + echo $tdWithClassLeft; + echo ($tableType=="table"? $lang['tbl'] : $lang['view']); + echo ""; + echo $tdWithClassLeft; + echo $params->getLink(array('table'=>$tableName, 'action'=>'row_view'), htmlencode($tableName)); + echo ""; + echo $tdWithClass; + echo $params->getLink(array('table'=>$tableName, 'action'=>'row_view'), $lang['browse']); + echo ""; + echo $tdWithClass; + echo $params->getLink(array('table'=>$tableName, 'action'=>'column_view'), $lang['struct']); + echo ""; + echo $tdWithClass; + echo $params->getLink(array('table'=>$tableName, 'action'=>'table_sql'), $lang['sql']); + echo ""; + echo $tdWithClass; + echo $params->getLink(array('table'=>$tableName, 'action'=>'table_search'), $lang['srch']); + echo ""; + echo $tdWithClass; + if($tableType=="table" && $db->isWritable() && $db->isDirWritable()) + echo $params->getLink(array('table'=>$tableName, 'action'=>'row_create'), $lang['insert']); + else + echo $lang['insert']; + echo ""; + echo $tdWithClass; + echo $params->getLink(array('table'=>$tableName, 'action'=>'table_export'), $lang['export']); + echo ""; + echo $tdWithClass; + if($tableType=="table" && $db->isWritable() && $db->isDirWritable()) + echo $params->getLink(array('table'=>$tableName, 'action'=>'table_import'), $lang['import']); + else + echo $lang['import']; + echo ""; + echo $tdWithClass; + if($db->isWritable() && $db->isDirWritable()) + echo $params->getLink(array('table'=>$tableName, 'action'=>'table_rename'), $lang['rename']); + else + echo $lang['rename']; + echo ""; + echo $tdWithClass; + if($tableType=="table" && $db->isWritable() && $db->isDirWritable()) + echo $params->getLink(array('table'=>$tableName, 'action'=>'table_empty'), $lang['empty'], 'empty'); + else + echo $lang['empty']; + echo ""; + echo $tdWithClass; + if($db->isWritable() && $db->isDirWritable()) + echo $params->getLink(array('table'=>$tableName, 'action'=>'table_drop'), $lang['drop'], 'drop'); + else + echo $lang['drop']; + echo ""; + echo $tdWithClass; + echo $records; + echo ""; + echo ""; } echo ""; - echo ""; - echo ""; + echo ""; + echo ""; echo ""; echo "
"; - echo "".$lang['type']." ".helpLink($lang['help3']); + echo $params->getLink(array('sort'=>'type', 'order'=>$orderTag), $lang['type']); + echo helpLink($lang['help3']); if(isset($_SESSION[COOKIENAME.'sortTables']) && $_SESSION[COOKIENAME.'sortTables']=="type") echo (($_SESSION[COOKIENAME.'orderTables']=="ASC") ? " " : " "); echo ""; - echo "".$lang['name'].""; + echo $params->getLink(array('sort'=>'name', 'order'=>$orderTag), $lang['name']); if(isset($_SESSION[COOKIENAME.'sortTables']) && $_SESSION[COOKIENAME.'sortTables']=="name") echo (($_SESSION[COOKIENAME.'orderTables']=="ASC") ? " " : " "); echo "".$lang['act']."".$lang['rec']."
"; $tdWithClassLeft = ""; - - if($result[$i]['type']=="table") - { - echo "
".sizeof($result)." ".$lang['total']."".$totalRecords.($skippedTables?" + ?":"")."".sizeof($tables)." ".$lang['total']."".$totalRecords.($skippedTables?" ".$params->getLink(array('forceCount'=>'1'),'+ ?'):"")."
"; echo "
"; if($skippedTables) - echo "
".sprintf($lang["counting_skipped"],"","")."
"; + echo "
".sprintf($lang["counting_skipped"],"'1'))."'>","")."
"; + } + if($db->isWritable() && $db->isDirWritable()) + { + echo "
"; + echo "".$lang['create_tbl_db']." '".htmlencode($db->getName())."'"; + echo $params->getForm(array('action'=>'table_create'), 'get'); + echo $lang['name'].": "; + echo $lang['fld_num'].": "; + echo ""; + echo ""; + echo "
"; + echo "
"; + echo "
"; + echo "".$lang['create_view']." '".htmlencode($db->getName())."'"; + echo $params->getForm(array('action'=>'view_create', 'confirm'=>'1')); + echo $lang['name'].": "; + echo $lang['sel_state']." ".helpLink($lang['help4']).": "; + echo ""; + echo ""; + echo "
"; } - echo "
"; - echo "".$lang['create_tbl_db']." '".htmlencode($db->getName())."'"; - echo "
"; - echo $token_html; - echo $lang['name'].": "; - echo $lang['fld_num'].": "; - echo ""; - echo "
"; - echo "
"; - echo "
"; - echo "
"; - echo "".$lang['create_view']." '".htmlencode($db->getName())."'"; - echo "
"; - echo $token_html; - echo $lang['name'].": "; - echo $lang['sel_state']." ".helpLink($lang['help4']).": "; - echo ""; - echo "
"; - echo "
"; } else if($view=="sql") { @@ -3943,11 +3935,11 @@ function drawChart() //save the queries in history if necessary if($maxSavedQueries!=0 && $maxSavedQueries!=false) { - if(!isset($_SESSION['query_history'])) - $_SESSION['query_history'] = array(); - $_SESSION['query_history'][md5(strtolower($queryStr))] = $queryStr; - if(sizeof($_SESSION['query_history']) > $maxSavedQueries) - array_shift($_SESSION['query_history']); + if(!isset($_SESSION[COOKIENAME.'query_history'])) + $_SESSION[COOKIENAME.'query_history'] = array(); + $_SESSION[COOKIENAME.'query_history'][md5(strtolower($queryStr))] = $queryStr; + if(sizeof($_SESSION[COOKIENAME.'query_history']) > $maxSavedQueries) + array_shift($_SESSION[COOKIENAME.'query_history']); } $query = explode_sql($delimiter, $queryStr); //explode the query string into individual queries based on the delimiter @@ -3965,9 +3957,10 @@ function drawChart() echo "
".$lang['err'].": ".htmlencode($db->getError())."
"; } echo "

"; - if($row = $db->fetch($table_result, 'assoc')) + if($row = $db->fetch($table_result, 'num')) { - $headers = array_keys($row); + for($j=0; $jgetColumnName($table_result,$j); echo ""; echo ""; for($j=0; $j"; $rowCount = 0; - for(; $rowCount==0 || $row = $db->fetch($table_result, 'assoc'); $rowCount++) + for(; $rowCount==0 || $row = $db->fetch($table_result, 'num'); $rowCount++) { $tdWithClass = ""; for($z=0; $zNULL"; else - echo htmlencode(subString($row[$headers[$z]])); + echo htmlencode(subString($row[$z])); echo ""; } echo ""; } $queryTimer->stop(); echo "
"; echo "


"; - - + + if($table_result !== NULL && $table_result !== false) { echo "
"; @@ -4014,7 +4007,7 @@ function drawChart() echo "
"; } - + } } } @@ -4027,18 +4020,18 @@ function drawChart() echo "
"; echo "".sprintf($lang['run_sql'],htmlencode($db->getName())).""; - echo "
"; - echo $token_html; - if(isset($_SESSION['query_history']) && sizeof($_SESSION['query_history'])>0) + echo $params->getForm(array('view'=>'sql')); + if(isset($_SESSION[COOKIENAME.'query_history']) && sizeof($_SESSION[COOKIENAME.'query_history'])>0) { echo "".$lang['recent_queries']."

"; } echo ""; + echo ""; echo $lang['delimit']." "; echo ""; echo "
"; @@ -4055,8 +4048,7 @@ function drawChart() printf($lang['db_vac'], htmlencode($db->getName())); echo "

"; } - echo "
"; - echo $token_html; + echo $params->getForm(array('view'=>'vacuum')); printf($lang['vac_desc'],htmlencode($db->getName())); echo "

"; echo ""; @@ -4065,31 +4057,28 @@ function drawChart() else if($view=="export") { //- Export view (=export) - echo ""; - echo $token_html; + echo $params->getForm(array('view'=>'export')); echo "
".$lang['export'].""; echo ""; echo "

"; echo ""; echo "
"; echo "
"; - + echo "
".$lang['options'].""; - echo " ".helpLink($lang['help5'])."
"; + echo " ".helpLink($lang['help5'])."
"; echo " ".helpLink($lang['help6'])."
"; - echo " ".helpLink($lang['help7'])."
"; + echo " ".helpLink($lang['help7'])."
"; echo " ".helpLink($lang['help8'])."
"; - echo " ".helpLink($lang['help9'])."
"; + echo " ".helpLink($lang['help9'])."
"; echo "
"; - + echo ""; - + echo "
"; echo "

"; echo "
".$lang['save_as'].""; @@ -4115,7 +4104,9 @@ function drawChart() echo " "; echo "
"; echo "
"; - echo "
".sprintf($lang['backup_hint'], "".$lang["backup_hint_linktext"]."")."
"; + echo "
".sprintf($lang['backup_hint'], + $params->getLink(array('download'=>$currentDB['path'], 'token'=>$_SESSION[COOKIENAME.'token']), $lang["backup_hint_linktext"], '', $lang['backup']) + )."
"; } else if($view=="import") { @@ -4129,27 +4120,25 @@ function drawChart() echo $importSuccess; echo "
"; } - - echo "
"; - echo $token_html; + + echo $params->getForm(array('view'=>'import'), 'post', true); echo "
".$lang['import'].""; echo ""; echo "
"; echo "
"; - + echo "
".$lang['options'].""; echo $lang['no_opt']; echo "
"; - + echo ""; - + echo "
"; echo "

"; - + echo "
".$lang['import_f'].""; - echo " "; + echo "".$lang['max_file_size'].": ".number_format(fileUploadMaxSize()/1024/1024)." MiB ".helpLink($lang['help11'])."
"; + echo ""; + echo ""; echo "
"; } else if($view=="rename") { //- Rename database confirmation (=rename) - if(isset($extension_not_allowed)) - { - echo "
"; - echo $lang['extension_not_allowed'].': '; - echo implode(', ', array_map('htmlencode', $allowed_extensions)); - echo '
'.$lang['add_allowed_extension']; - echo "

"; - } - if(isset($dbexists)) - { - echo "
"; - if($oldpath==$newpath) - echo $lang['err'].": ".$lang['warn_dumbass']; - else{ - echo $lang['err'].": "; - printf($lang['db_exists'], htmlencode($newpath)); - } - echo "

"; - } - if(isset($justrenamed)) - { - echo "
"; - printf($lang['db_renamed'], htmlencode($oldpath)); - echo " '".htmlencode($newpath)."'."; - echo "

"; - } - echo ""; - echo $token_html; + echo $params->getForm(array('view'=>'rename', 'database_rename'=>'1')); echo ""; echo $lang['db_rename']." '".htmlencode($db->getPath())."' ".$lang['to']." "; - echo "
"; + echo ""; } else if($view=="delete") { //- Delete database confirmation (=delete) - echo "
"; - echo $token_html; + echo $params->getForm(array('database_delete'=>'1')); echo "
"; echo sprintf($lang['ques_del_db'],htmlencode($db->getPath()))."

"; echo ""; echo " "; - echo "".$lang['cancel'].""; + echo $params->getLink(array(), $lang['cancel']); echo "
"; - echo "
"; + echo ""; } echo ""; } +echo ""; //- HTML: page footer echo "
"; @@ -4243,6 +4207,7 @@ function drawChart() // Authorization class // Maintains user's logged-in state and security of application // + class Authorization { private $authorized; @@ -4251,12 +4216,17 @@ class Authorization public function __construct() { + // first, make sure a CSRF token is generated + $this->generateToken(); + // second, check for possible CSRF attacks. to protect logins, this is done before checking login + $this->checkToken(); + // the salt and password encrypting is probably unnecessary protection but is done just // for the sake of being very secure if(!isset($_SESSION[COOKIENAME.'_salt']) && !isset($_COOKIE[COOKIENAME.'_salt'])) { // create a random salt for this session if a cookie doesn't already exist for it - $_SESSION[COOKIENAME.'_salt'] = self::generateSalt(20); + $_SESSION[COOKIENAME.'_salt'] = self::generateSalt(22); } else if(!isset($_SESSION[COOKIENAME.'_salt']) && isset($_COOKIE[COOKIENAME.'_salt'])) { @@ -4271,14 +4241,15 @@ public function __construct() // no password SYSTEMPASSWORD == '' // correct password stored in session - || isset($_SESSION[COOKIENAME.'password']) && $_SESSION[COOKIENAME.'password'] == $this->system_password_encrypted + || isset($_SESSION[COOKIENAME.'password']) && hash_equals($_SESSION[COOKIENAME.'password'], $this->system_password_encrypted) // correct password stored in cookie - || isset($_COOKIE[COOKIENAME]) && isset($_COOKIE[COOKIENAME.'_salt']) && md5(SYSTEMPASSWORD."_".$_COOKIE[COOKIENAME.'_salt']) == $_COOKIE[COOKIENAME]; + || isset($_COOKIE[COOKIENAME]) && isset($_COOKIE[COOKIENAME.'_salt']) && hash_equals(md5(SYSTEMPASSWORD."_".$_COOKIE[COOKIENAME.'_salt']), $_COOKIE[COOKIENAME]); } public function attemptGrant($password, $remember) { - if ($password == SYSTEMPASSWORD) { + $hashed_password = crypt(SYSTEMPASSWORD, '$2a$07$'.self::generateSalt(22).'$'); + if (hash_equals($hashed_password, crypt($password, $hashed_password))) { if ($remember) { // user wants to be remembered, so set a cookie $expire = time()+60*60*24*30; //set expiration to 1 month from now @@ -4311,6 +4282,9 @@ public function revoke() session_unset(); session_destroy(); $this->authorized = false; + // start a new session and generate a new CSRF token for the login form + session_start(); + $this->generateToken(); } public function isAuthorized() @@ -4338,6 +4312,48 @@ private static function generateSalt($saltSize) } return $salt; } + + private function generateToken() + { + // generate CSRF token + if (empty($_SESSION[COOKIENAME.'token'])) + { + if (function_exists('random_bytes')) // introduced in PHP 7.0 + { + $_SESSION[COOKIENAME.'token'] = bin2hex(random_bytes(32)); + } + elseif (function_exists('openssl_random_pseudo_bytes')) // introduced in PHP 5.3.0 + { + $_SESSION[COOKIENAME.'token'] = bin2hex(openssl_random_pseudo_bytes(32)); + } + else + { + // For PHP 5.2.x - This case can be removed once we drop support for 5.2.x + $_SESSION[COOKIENAME.'token'] = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); + } + } + } + + private function checkToken() + { + // checking CSRF token + if($_SERVER['REQUEST_METHOD'] === 'POST' || isset($_GET['download'])) // all POST forms need tokens! downloads are protected as well + { + if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['token'])) + $check_token=$_POST['token']; + elseif($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['token'])) + $check_token=$_GET['token']; + + if (!isset($check_token)) + { + die("CSRF token missing"); + } + elseif(!hash_equals($_SESSION[COOKIENAME.'token'], $check_token)) + { + die("CSRF token is wrong - please try to login again"); + } + } + } } // Database class @@ -4350,10 +4366,11 @@ class Database protected $data; protected $lastResult; protected $alterError; + protected $debugOutput =''; public function __construct($data) { - global $lang; + global $lang, $params; $this->data = $data; try { @@ -4361,8 +4378,7 @@ public function __construct($data) { echo "
"; printf($lang['db_not_writeable'], htmlencode($this->data["path"]), htmlencode(dirname($this->data["path"]))); - echo "
"; - echo ''; + echo $params->getForm(); echo ""; echo "
"; echo "

"; @@ -4373,7 +4389,7 @@ public function __construct($data) switch(true) { - case ((!isset($data['type']) || $data['type']!=2) && (FORCETYPE=="PDO" || (FORCETYPE==false && class_exists("PDO") && ($ver==-1 || $ver==3)))): + case ((!isset($data['type']) || $data['type']!=2) && (FORCETYPE=="PDO" || (FORCETYPE==false && class_exists("PDO") && in_array("sqlite", PDO::getAvailableDrivers()) && ($ver==-1 || $ver==3)))): $this->db = new PDO("sqlite:".$this->data['path']); if($this->db!=NULL) { @@ -4415,36 +4431,56 @@ public function registerUserFunction($ids) if ($this->type == 'PDO') { foreach ($ids as $id) { - $this->db->sqliteCreateFunction($id, $id, 1); + $this->db->sqliteCreateFunction($id, $id, -1); } } else { // type is Sqlite3 or SQLiteDatabase foreach ($ids as $id) { - $this->db->createFunction($id, $id, 1); + $this->db->createFunction($id, $id, -1); } } } - public function getError() + public function getError($complete_msg = false) { + global $lang, $debug; + $error = "unknown"; + if($this->alterError!='') { $error = $this->alterError; $this->alterError = ""; - return $error; } else if($this->type=="PDO") { $e = $this->db->errorInfo(); - return $e[2]; + $error = $e[2]; } else if($this->type=="SQLite3") { - return $this->db->lastErrorMsg(); + $error = $this->db->lastErrorMsg(); } else { - return sqlite_error_string($this->db->lastError()); + $error = sqlite_error_string($this->db->lastError()); } + + if($complete_msg) + { + $error = $lang['err'].": ".htmlencode($error); + // do not suggest to report a bug when constraints fail + if(strpos($error, 'constraint failed')===false) + $error.="
".$lang['bug_report'].' '.PROJECT_BUGTRACKER_LINK; + } + + if($debug) + $error .= $this->getDebugOutput(); + + return $error; + } + + function getDebugOutput() + { + return ($this->debugOutput != "" ? "
DEBUG:
".$this->debugOutput : $this->debugOutput); } public function showError() @@ -4469,7 +4505,7 @@ public function showError() printf($lang['sqlite_ext_support'], PROJECT); else { - if(!$classPDO && !$classSQLite3 && $this->getVersion()==3) + if(!$PDOSqliteDriver && !$classSQLite3 && $this->getVersion()==3) printf($lang['sqlite_v_error'], 3, PROJECT, 2); else if(!$classSQLiteDatabase && $this->getVersion()==2) printf($lang['sqlite_v_error'], 2, PROJECT, 3); @@ -4486,7 +4522,7 @@ public function showError() // print the list of databases public function print_db_list() { - global $databases, $lang; + global $databases, $lang, $params, $currentDB; echo "
".$lang['db_ch'].""; if(sizeof($databases)<10) //if there aren't a lot of databases, just show them as a list of links instead of drop down menu { @@ -4495,32 +4531,32 @@ public function print_db_list() { $i++; $name = $database['name']; - if(strlen($name)>25) - $name = "...".substr($name, strlen($name)-22, 22); + if(mb_strlen($name)>25) + $name = "...".mb_substr($name, mb_strlen($name)-22, 22); echo '[' . ($database['readable'] ? 'r':' ' ) . ($database['writable'] && $database['writable_dir'] ? 'w':' ' ) . '] '; - if($database == $_SESSION[COOKIENAME.'currentDB']) - echo "".htmlencode($name)."  [↓]"; - else - echo "".htmlencode($name)."  [↓]"; + + echo $params->getLink(array('database'=>$database['path'], 'table'=>null), htmlencode($name), ($database == $currentDB? 'active_db': '') ); + echo "  "; + echo $params->getLink(array('download'=>$database['path'], 'table'=>null, 'token'=>$_SESSION[COOKIENAME.'token']), '[↓]', '', $lang['backup']); + if($i"; } } else //there are a lot of databases - show a drop down menu { - echo "
"; - echo ''; - echo ""; foreach($databases as $database) { $perms_string = htmlencode('[' . ($database['readable'] ? 'r':' ' ) . ($database['writable'] && $database['writable_dir'] ? 'w':' ' ) . '] '); - if($database == $_SESSION[COOKIENAME.'currentDB']) + if($database == $currentDB) echo ""; else echo ""; } - echo " "; - echo ""; + echo ""; + echo ""; echo "
"; } echo "
"; @@ -4587,7 +4623,7 @@ public function getVersion() } } - //get the size of the database (in KB) + //get the size of the database (in KiB) public function getSize() { return round(filesize($this->data["path"])*0.0009765625, 1); @@ -4620,7 +4656,46 @@ public function getTypeOfTable($table) $result = $this->select("SELECT `type` FROM `sqlite_master` WHERE `name`=" . $this->quote($table), 'assoc'); return $result['type']; } - + + public function getTableInfo($table) + { + return $this->selectArray("PRAGMA table_info(".$this->quote_id($table).")"); + } + + // returns the list of tables (opt. incl. views) as + // array( Tablename => tableType ) with tableType being 'view' or 'table' + public function getTables($alsoViews=true, $alsoInternal=false, $orderBy='name', $orderDirection='ASC') + { + $query = "SELECT name, type FROM sqlite_master " + . "WHERE (type='table'".($alsoViews?" OR type='view'":"").") " + . "AND name!='' ".($alsoInternal? "":" AND name NOT LIKE 'sqlite_%' ") + . "ORDER BY ".$this->quote_id($orderBy)." ".$orderDirection; + $result = $this->selectArray($query); + $list = array(); + for($i=0; $i array(columName) ) + public function getTableDefinitions() + { + $tables = $this->getTables(true, true); + $result = array(); + foreach ($tables as $tableName => $tableType) + { + $tableInfo = $this->getTableInfo($tableName); + $columns = array(); + foreach($tableInfo as $column) + $columns[] = $column['name']; + $result[$tableName] = $columns; + } + return $result; + } + public function close() { if($this->type=="PDO") @@ -4701,7 +4776,9 @@ public function select($query, $mode="both") $mode = PDO::FETCH_NUM; else $mode = PDO::FETCH_BOTH; - return $result->fetch($mode); + $ret = $result->fetch($mode); + $result->closeCursor(); + return $ret; } else if($this->type=="SQLite3") { @@ -4711,7 +4788,9 @@ public function select($query, $mode="both") $mode = SQLITE3_NUM; else $mode = SQLITE3_BOTH; - return $result->fetchArray($mode); + $ret = $result->fetchArray($mode); + $result->finalize(); + return $ret; } else if($this->type=="SQLiteDatabase") { @@ -4742,7 +4821,9 @@ public function selectArray($query, $mode="both") $mode = PDO::FETCH_NUM; else $mode = PDO::FETCH_BOTH; - return $result->fetchAll($mode); + $ret = $result->fetchAll($mode); + $result->closeCursor(); + return $ret; } else if($this->type=="SQLite3") { @@ -4759,6 +4840,7 @@ public function selectArray($query, $mode="both") $arr[$i] = $res; $i++; } + $result->finalize(); return $arr; } else if($this->type=="SQLiteDatabase") @@ -4812,6 +4894,26 @@ public function fetch($result, $mode="both") return $result->fetch($mode); } } + + public function getColumnName($result, $colNum) + { + //make sure the result is valid + if($result=== false || $result===NULL || !is_object($result)) + return ""; // error or no rows returned + if($this->type=="PDO") + { + $meta = $result->getColumnMeta($colNum); + return $meta['name']; + } + else if($this->type=="SQLite3") + { + return $result->columnName($colNum); + } + else if($this->type=="SQLiteDatabase") + { + return $result->fieldName($colNum); + } + } // SQlite supports multiple ways of surrounding names in quotes: @@ -4883,7 +4985,7 @@ public function alterTable($table, $alterdefs) global $debug, $lang; $this->alterError=""; $errormsg = sprintf($lang['alter_failed'],htmlencode($table)).' - '; - if($debug) echo "ALTER TABLE: table=($table), alterdefs=($alterdefs), PCRE version=(".PCRE_VERSION.")

"; + if($debug) $this->debugOutput .= "ALTER TABLE: table=($table), alterdefs=($alterdefs), PCRE version=(".PCRE_VERSION.")

"; if($alterdefs != '') { $recreateQueries = array(); @@ -4891,20 +4993,43 @@ public function alterTable($table, $alterdefs) if(sizeof($resultArr)<1) { $this->alterError = $errormsg . sprintf($lang['tbl_inexistent'], htmlencode($table)); - if($debug) echo "ERROR: unknown table

"; + if($debug) $this->debugOutput .= "ERROR: unknown table

"; return false; } for($i=0; $i"; + if($debug) $this->debugOutput .= "recreate=(".$row['sql'].";)
"; + } + } + elseif($row['type']=='view') // workaround to rename views + { + $origsql = $row['sql']; + $preg_remove_create_view = "/^\s*+CREATE\s++VIEW\s++".$this->sqlite_surroundings_preg($table)."\s*+(AS\s++SELECT\s++.*+)$/is"; + $origsql_no_create = preg_replace($preg_remove_create_view, '$1', $origsql, 1); + if($debug) $this->debugOutput .= "origsql=($origsql)
preg_remove_create_table=($preg_remove_create_view)
"; + preg_match("/RENAME\s++TO\s++(?:\"((?:[^\"]|\"\")+)\"|'((?:[^']|'')+)')/is", $alterdefs, $matches); + if(isset($matches[1]) && $matches[1]!='') + $newname = $matches[1]; + elseif(isset($matches[2]) && $matches[2]!='') + $newname = $matches[2]; + else + { + $this->alterError = $errormsg . ' could not detect new view name. It needs to be in single or double quotes.'; + if($debug) $this->debugOutput .= "ERROR: could not detect new view name
"; + return false; } + $dropoldSQL = 'DROP VIEW '.$this->quote_id($table); + $createnewSQL = 'CREATE VIEW '.$this->quote_id($newname).' '.$origsql_no_create; + $alter_transaction = 'BEGIN; ' . $dropoldSQL .'; '. $createnewSQL . '; ' . 'COMMIT;'; + if($debug) $this->debugOutput .= $alter_transaction; + return $this->multiQuery($alter_transaction); } else { @@ -4913,15 +5038,15 @@ public function alterTable($table, $alterdefs) $origsql = $row['sql']; $preg_remove_create_table = "/^\s*+CREATE\s++TABLE\s++".$this->sqlite_surroundings_preg($table)."\s*+(\(.*+)$/is"; $origsql_no_create = preg_replace($preg_remove_create_table, '$1', $origsql, 1); - if($debug) echo "origsql=($origsql)
preg_remove_create_table=($preg_remove_create_table)
"; + if($debug) $this->debugOutput .= "origsql=($origsql)
preg_remove_create_table=($preg_remove_create_table)
"; if($origsql_no_create == $origsql) { $this->alterError = $errormsg . $lang['alter_tbl_name_not_replacable']; - if($debug) echo "ERROR: could not get rid of CREATE TABLE
"; + if($debug) $this->debugOutput .= "ERROR: could not get rid of CREATE TABLE
"; return false; } $createtemptableSQL = "CREATE TABLE ".$this->quote($tmpname)." ".$origsql_no_create; - if($debug) echo "createtemptableSQL=($createtemptableSQL)
"; + if($debug) $this->debugOutput .= "createtemptableSQL=($createtemptableSQL)
"; $createindexsql = array(); $preg_alter_part = "/(?:DROP(?! PRIMARY KEY)|ADD(?! PRIMARY KEY)|CHANGE|RENAME TO|ADD PRIMARY KEY|DROP PRIMARY KEY)" // the ALTER command ."(?:" @@ -4930,12 +5055,11 @@ public function alterTable($table, $alterdefs) ."\s+".$this->sqlite_surroundings_preg("+",false,",'\"\[`") // column names and stuff like this .")*/i"; if($debug) - echo "preg_alter_part=(".$preg_alter_part.")
"; + $this->debugOutput .= "preg_alter_part=(".$preg_alter_part.")
"; preg_match_all($preg_alter_part,$alterdefs,$matches); $defs = $matches[0]; - $get_oldcols_query = "PRAGMA table_info(".$this->quote_id($table).")"; - $result_oldcols = $this->selectArray($get_oldcols_query); + $result_oldcols = $this->getTableInfo($table); $newcols = array(); $coltypes = array(); $primarykey = array(); @@ -4960,12 +5084,12 @@ public function alterTable($table, $alterdefs) if(count($defs)<1) { $this->alterError = $errormsg . $lang['alter_no_def']; - if($debug) echo "ERROR: defs<1

"; + if($debug) $this->debugOutput .= "ERROR: defs<1

"; return false; } foreach($defs as $def) { - if($debug) echo "
def=$def
"; + if($debug) $this->debugOutput .= "
def=$def
"; $preg_parse_def = "/^(DROP(?! PRIMARY KEY)|ADD(?! PRIMARY KEY)|CHANGE|RENAME TO|ADD PRIMARY KEY|DROP PRIMARY KEY)" // $matches[1]: command ."(?:" // this is either @@ -4979,24 +5103,24 @@ public function alterTable($table, $alterdefs) ."(" // $matches[5]: anything after the column name ."(?:\s+'((?:[^']|'')+)')?" // $matches[6] (optional): a second column name surrounded with single quotes // (the match does not contain the quotes) - ."\s+" - ."((?:[A-Z]+\s*)+(?:\(\s*[+-]?\s*[0-9]+(?:\s*,\s*[+-]?\s*[0-9]+)?\s*\))?)\s*" // $matches[7]: a type name + ."\s*" + ."((?:[A-Z]+\s*)+(?:\(\s*[+-]?\s*[0-9]+(?:\s*,\s*[+-]?\s*[0-9]+)?\s*\))?)?\s*" // $matches[7] (optional): a type name .".*". ")" ."?\s*$" .")?\s*$/i"; // in case of DROP PRIMARY KEY, there is nothing after the command - if($debug) echo "preg_parse_def=$preg_parse_def
"; + if($debug) $this->debugOutput .= "preg_parse_def=$preg_parse_def
"; $parse_def = preg_match($preg_parse_def,$def,$matches); if($parse_def===false) { $this->alterError = $errormsg . $lang['alter_parse_failed']; - if($debug) echo "ERROR: !parse_def

"; + if($debug) $this->debugOutput .= "ERROR: !parse_def

"; return false; } if(!isset($matches[1])) { $this->alterError = $errormsg . $lang['alter_action_not_recognized']; - if($debug) echo "ERROR: !isset(matches[1])

"; + if($debug) $this->debugOutput .= "ERROR: !isset(matches[1])

"; return false; } $action = strtolower($matches[1]); @@ -5013,7 +5137,7 @@ public function alterTable($table, $alterdefs) $column_escaped = str_replace("'","''",$column); - if($debug) echo "action=($action), column=($column), column_escaped=($column_escaped)
"; + if($debug) $this->debugOutput .= "action=($action), column=($column), column_escaped=($column_escaped)
"; /* we build a regex that devides the CREATE TABLE statement parts: Part example Group Explanation @@ -5026,7 +5150,7 @@ public function alterTable($table, $alterdefs) $preg_column_definiton = "\s*+".$this->sqlite_surroundings_preg("+",true," '\"\[`,",$column)."(?:\s*+".$this->sqlite_surroundings_preg("*",false,"'\",`\[ ").")++"; // catches a complete column definition, even if it is // 'column' TEXT NOT NULL DEFAULT 'we have a comma, here and a double ''quote!' // this definition does NOT match columns with the column name $column - if($debug) echo "preg_column_definition=(".$preg_column_definiton.")
"; + if($debug) $this->debugOutput .= "preg_column_definition=(".$preg_column_definiton.")
"; $preg_columns_before = // columns before the one changed/dropped (keep) "(?:". "(". // group $2. Keep this one unchanged! @@ -5037,7 +5161,7 @@ public function alterTable($table, $alterdefs) ")". // end of group $2 ",\s*+" // the last comma of the last column before the column to change. Do not keep it! .")?"; // there might be no columns before - if($debug) echo "preg_columns_before=(".$preg_columns_before.")
"; + if($debug) $this->debugOutput .= "preg_columns_before=(".$preg_columns_before.")
"; $preg_columns_after = "(,\s*(.+))?"; // the columns after the column to drop. This is group $3 (drop) or $4(change) (keep!) // we could remove the comma using $6 instead of $5, but then we might have no comma at all. // Keeping it leaves a problem if we drop the first column, so we fix that case in another regex. @@ -5060,9 +5184,9 @@ public function alterTable($table, $alterdefs) $preg_error = $this->getPregError(); if($debug) { - echo $createtesttableSQL."

"; - echo $newSQL."

"; - echo $preg_pattern_add."

"; + $this->debugOutput .= $createtesttableSQL."

"; + $this->debugOutput .= $newSQL."

"; + $this->debugOutput .= $preg_pattern_add."

"; } if($newSQL==$createtesttableSQL) // pattern did not match, so column adding did not succed { @@ -5072,13 +5196,17 @@ public function alterTable($table, $alterdefs) $createtesttableSQL = $newSQL; break; case 'change': - if(!isset($matches[6]) || !isset($matches[7])) + var_dump($matches); + if(!isset($matches[6])) { $this->alterError = $errormsg . ' (change) - '.$lang['alter_col_not_recognized']; return false; } $new_col_name = $matches[6]; - $new_col_type = $matches[7]; + if(!isset($matches[7])) + $new_col_type = ''; + else + $new_col_type = $matches[7]; $new_col_definition = "'$new_col_name' $new_col_type"; $preg_column_to_change = "\s*".$this->sqlite_surroundings_preg($column)."(?:\s+".preg_quote($coltypes[$column]).")?(\s+(?:".$this->sqlite_surroundings_preg("*",false,",'\"`\[").")+)?"; // replace this part (we want to change this column) @@ -5093,11 +5221,11 @@ public function alterTable($table, $alterdefs) $newSQL = preg_replace("/^\s*(CREATE\s+TABLE\s+".preg_quote($this->quote($tmpname),"/")."\s+\(),\s*/",'$1',$newSQL); if($debug) { - echo "preg_column_to_change=(".$preg_column_to_change.")

"; - echo $createtesttableSQL."

"; - echo $newSQL."

"; + $this->debugOutput .= "preg_column_to_change=(".$preg_column_to_change.")

"; + $this->debugOutput .= $createtesttableSQL."

"; + $this->debugOutput .= $newSQL."

"; - echo $preg_pattern_change."

"; + $this->debugOutput .= $preg_pattern_change."

"; } if($newSQL==$createtesttableSQL || $newSQL=="") // pattern did not match, so column removal did not succed { @@ -5119,9 +5247,9 @@ public function alterTable($table, $alterdefs) $newSQL = preg_replace("/^\s*(CREATE\s+TABLE\s+".preg_quote($this->quote($tmpname),"/")."\s+\(),\s*/",'$1',$newSQL); if($debug) { - echo $createtesttableSQL."

"; - echo $newSQL."

"; - echo $preg_pattern_drop."

"; + $this->debugOutput .= $createtesttableSQL."

"; + $this->debugOutput .= $newSQL."

"; + $this->debugOutput .= $preg_pattern_drop."

"; } if($newSQL==$createtesttableSQL || $newSQL=="") // pattern did not match, so column removal did not succed { @@ -5144,12 +5272,12 @@ public function alterTable($table, $alterdefs) break; case 'drop primary key': // we want to drop the primary key - if($debug) echo "DROP"; + if($debug) $this->debugOutput .= "DROP"; if(sizeof($primarykey)==1) { // if not compound primary key, might be a column constraint -> try removal $column = $primarykey[0]; - if($debug) echo "
Trying to drop column constraint for column $column
"; + if($debug) $this->debugOutput .= "
Trying to drop column constraint for column $column
"; /* TODO: This does not work yet: CREATE TABLE 't12' ('t1' INTEGER CONSTRAINT "bla" NOT NULL CONSTRAINT 'pk' PRIMARY KEY ); ALTER TABLE "t12" DROP PRIMARY KEY @@ -5179,20 +5307,20 @@ public function alterTable($table, $alterdefs) $newSQL = preg_replace("/^\s*(CREATE\s+TABLE\s+".preg_quote($this->quote($tmpname),"/")."\s+\(),\s*/",'$1',$newSQL); if($debug) { - echo "preg_column_to_change=(".$preg_column_to_change.")

"; - echo $createtesttableSQL."

"; - echo $newSQL."

"; + $this->debugOutput .= "preg_column_to_change=(".$preg_column_to_change.")

"; + $this->debugOutput .= $createtesttableSQL."

"; + $this->debugOutput .= $newSQL."

"; - echo $preg_pattern_change."

"; + $this->debugOutput .= $preg_pattern_change."

"; } if($newSQL!=$createtesttableSQL && $newSQL!="") // pattern did match, so PRIMARY KEY constraint removed :) { $createtesttableSQL = $newSQL; - if($debug) echo "
SUCCEEDED
"; + if($debug) $this->debugOutput .= "
SUCCEEDED
"; } else { - if($debug) echo "NO LUCK"; + if($debug) $this->debugOutput .= "NO LUCK"; // TODO: try removing table constraint return false; } @@ -5203,7 +5331,7 @@ public function alterTable($table, $alterdefs) break; default: - if($debug) echo 'ERROR: unknown alter operation!

'; + if($debug) $this->debugOutput .= 'ERROR: unknown alter operation!

'; $this->alterError = $errormsg . $lang['alter_unknown_operation']; return false; } @@ -5242,7 +5370,7 @@ public function alterTable($table, $alterdefs) { if(!isset($newcols[$indexInfo['name']])) { - if($debug) echo 'Not recreating the following index:

'.htmlencode($recreate_query['sql']).'

'; + if($debug) $this->debugOutput .= 'Not recreating the following index:

'.htmlencode($recreate_query['sql']).'

'; // Index on a column that was dropped. Skip recreation. continue 2; } @@ -5267,7 +5395,7 @@ public function alterTable($table, $alterdefs) else { // the CREATE INDEX regex did not match. this normally should not happen - if($debug) echo 'ERROR: CREATE INDEX regex did not match!?

'; + if($debug) $this->debugOutput .= 'ERROR: CREATE INDEX regex did not match!?

'; // just try to recreate the index originally (will fail most likely) $alter_transaction .= $recreate_query['sql'].';'; } @@ -5278,13 +5406,13 @@ public function alterTable($table, $alterdefs) $alter_transaction .= $recreate_query['sql'].';'; break; default: - if($debug) echo 'ERROR: Unknown type '.htmlencode($recreate_query['type']).'

'; + if($debug) $this->debugOutput .= 'ERROR: Unknown type '.htmlencode($recreate_query['type']).'

'; $alter_transaction .= $recreate_query['sql'].';'; } } } $alter_transaction .= 'COMMIT;'; - if($debug) echo $alter_transaction; + if($debug) $this->debugOutput .= $alter_transaction; return $this->multiQuery($alter_transaction); } } @@ -5306,8 +5434,7 @@ public function multiQuery($query) // checks whether a table has a primary key public function hasPrimaryKey($table) { - $query = "PRAGMA table_info(".$this->quote_id($table).")"; - $table_info = $this->selectArray($query); + $table_info = $this->getTableInfo($table); foreach($table_info as $row_id => $row_data) { if($row_data['pk']) @@ -5333,12 +5460,14 @@ public function getPrimaryKey($table) else { // the table is without rowid, so use the primary key - $query = "PRAGMA table_info(".$this->quote_id($table).")"; - $table_info = $this->selectArray($query); - foreach($table_info as $row_id => $row_data) + $table_info = $this->getTableInfo($table); + if(is_array($table_info)) { - if($row_data['pk']) - $primary_key[] = $row_data['name']; + foreach($table_info as $row_id => $row_data) + { + if($row_data['pk']) + $primary_key[] = $row_data['name']; + } } } return $primary_key; @@ -5416,11 +5545,120 @@ public function import_sql($query) return true; } + public function prepareQuery($query) + { + if($this->type=='PDO' || $this->type=='SQLite3') + return $this->db->prepare($query); + else + { + // here we are in trouble, SQLiteDatabase cannot prepare statements. + // we need to emulate prepare as best as we can + # todo: implement this + return null; + } + } + + public function bindValue($handle, $parameter, $value, $type) + { + if($this->type=='SQLite3') + { + $types = array( + 'bool'=>SQLITE3_INTEGER, + 'int'=>SQLITE3_INTEGER, + 'float'=>SQLITE3_FLOAT, + 'text'=>SQLITE3_TEXT, + 'blob'=>SQLITE3_BLOB, + 'null'=>SQLITE3_NULL); + if(!isset($types[$type])) + $type = 'text'; + // there is no SQLITE_BOOL, so check value and make sure it is 0/1 + if($type=='bool') + { + if($value===1 || $value===true) + $value=1; + elseif($value===0 || $value===false) + $value=0; + else + return false; + } + return $handle->bindValue($parameter, $value, $types[$type]); + } + if($this->type=='PDO') + { + $types = array( + 'bool'=>PDO::PARAM_BOOL, + 'int'=>PDO::PARAM_INT, + 'float'=>PDO::PARAM_STR, + 'text'=>PDO::PARAM_STR, + 'blob'=>PDO::PARAM_LOB, + 'null'=>PDO::PARAM_NULL); + if(!isset($types[$type])) + $type = 'text'; + // there is no PDO::PARAM_FLOAT, so we check it ourself + if($type=='float') + { + if(is_numeric($value)) + $value = (float) $value; + else + return false; + } + return $handle->bindValue($parameter, $value, $types[$type]); + } + else + # todo: workaround + return false; + + } + + public function executePrepared($handle, $fetchResult=false) + { + if($this->type=='PDO') + { + $ok=$handle->execute(); + if($fetchResult && $ok) + { + $res = $handle->fetchAll(); + $handle->closeCursor(); + return $res; + } + else + { + if($ok) + $handle->closeCursor(); + return $ok; + } + } + elseif($this->type=='SQLite3') + { + $resultset=$handle->execute(); + if($fetchResult && $resultset!==false) + { + $res = $resultset->fetchArray(); + $resultset->finalize(); + return $res; + } + else + { + if($resultset!==false) + $resultset->finalize(); + if($resultset===false) + return false; + else + return true; + } + } + else + { + #todo. + return false; + } + } + //import csv //returns true on success, error message otherwise public function import_csv($filename, $table, $field_terminate, $field_enclosed, $field_escaped, $null, $fields_in_first_row) { - // CSV import implemented by Christopher Kramer - http://www.christosoft.de + @set_time_limit(-1); $csv_handle = fopen($filename,'r'); $csv_insert = "BEGIN;\n"; $csv_number_of_rows = 0; @@ -5428,39 +5666,57 @@ public function import_csv($filename, $table, $field_terminate, $field_enclosed, if($field_enclosed=="") $field_enclosed='"'; // PHP requires escaper defined if($field_escaped=="") $field_escaped='\\'; + // support tab delimiters + if($field_terminate=='\t') $field_terminate = "\t"; while($csv_handle!==false && !feof($csv_handle)) { $csv_data = fgetcsv($csv_handle, 0, $field_terminate, $field_enclosed, $field_escaped); - if($csv_data[0] != NULL || count($csv_data)>1) + if(is_array($csv_data) && ($csv_data[0] != NULL || count($csv_data)>1)) { $csv_number_of_rows++; - if($fields_in_first_row && $csv_number_of_rows==1) + if($csv_number_of_rows==1) { - $fields_in_first_row = false; - continue; - } - $csv_col_number = count($csv_data); + if($this->getTypeOfTable($table)!="table") + { + // First,Create a new table + $csv_insert .="CREATE TABLE ".$this->quote($table)." ("; + $number_of_cols = count($csv_data); + foreach($csv_data as $csv_col => $csv_cell) + { + if($fields_in_first_row) + $csv_insert .= $this->quote($csv_cell); + else + $csv_insert.= $this->quote("col{$csv_col}"); + if($csv_col < $number_of_cols-1) + $csv_insert .= ", "; + } + $csv_insert .=");"; + + } else { + $number_of_cols = count($this->getTableInfo($table)); + } + if($fields_in_first_row) + continue; + } $csv_insert .= "INSERT INTO ".$this->quote_id($table)." VALUES ("; - foreach($csv_data as $csv_col => $csv_cell) + for($csv_col = 0; $csv_col < $number_of_cols; $csv_col++) { - if($csv_cell == $null) $csv_insert .= "NULL"; + if(isset($csv_data[$csv_col])) + $csv_cell = $csv_data[$csv_col]; + else + $csv_cell = $null; + if($csv_cell == $null) + $csv_insert .= "NULL"; else - { $csv_insert.= $this->quote($csv_cell); - } - if($csv_col == $csv_col_number-2 && $csv_data[$csv_col+1]=='') - { - // the CSV row ends with the separator (like old phpliteadmin exported) - break; - } - if($csv_col < $csv_col_number-1) $csv_insert .= ","; + if($csv_col < $number_of_cols-1) + $csv_insert .= ","; } $csv_insert .= ");\n"; - if($csv_number_of_rows > 5000) + if($csv_number_of_rows % 5000 == 0) { $csv_insert .= "COMMIT;\nBEGIN;\n"; - $csv_number_of_rows = 0; } } } @@ -5482,6 +5738,9 @@ public function import_csv($filename, $table, $field_terminate, $field_enclosed, public function export_csv($tables, $field_terminate, $field_enclosed, $field_escaped, $null, $crlf, $fields_in_first_row) { @set_time_limit(-1); + // we use \r\n if the _client_ OS is windows (as the exported file is downloaded to the client), \n otherwise + $crlf = (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'Win')!==false ? "\r\n" : "\n"); + $query = "SELECT * FROM sqlite_master WHERE type='table' or type='view' ORDER BY type DESC"; $result = $this->selectArray($query); for($i=0; $iquote_id($result[$i]['tbl_name']).")"; - $temp = $this->selectArray($query); + $temp = $this->getTableInfo($result[$i]['tbl_name']); $cols = array(); for($z=0; $zquote_id($result[$i]['tbl_name']); $table_result = $this->query($query); @@ -5516,7 +5774,7 @@ public function export_csv($tables, $field_terminate, $field_enclosed, $field_es while($row = $this->fetch($table_result, "assoc")) { if(!$firstRow) - echo "\r\n"; + echo $crlf; else $firstRow=false; @@ -5541,7 +5799,7 @@ public function export_csv($tables, $field_terminate, $field_enclosed, $field_es } } if($igetPath()."\r\n"; - echo "----\r\n"; + echo "----".$crlf; + echo "-- ".PROJECT." ".$lang['db_dump']." (".PROJECT_URL.")".$crlf; + echo "-- ".PROJECT." ".$lang['ver'].": ".VERSION.$crlf; + echo "-- ".$lang['exported'].": ".date($lang['date_format']).$crlf; + echo "-- ".$lang['db_f'].": ".$this->getPath().$crlf; + echo "----".$crlf; } $query = "SELECT * FROM sqlite_master WHERE type='table' OR type='index' OR type='view' OR type='trigger' ORDER BY type='trigger', type='index', type='view', type='table'"; $result = $this->selectArray($query); if($transaction) - echo "BEGIN TRANSACTION;\r\n"; + echo "BEGIN TRANSACTION;".$crlf; //iterate through each table for($i=0; $iquote_id($result[$i]['name']).";\r\n"; + echo "DROP ".strtoupper($result[$i]['type'])." IF EXISTS ".$this->quote_id($result[$i]['name']).";".$crlf; } if($structure) { if($comments) { - echo "\r\n----\r\n"; + echo "\r\n----".$crlf; if($result[$i]['type']=="table" || $result[$i]['type']=="view") - echo "-- ".ucfirst($result[$i]['type'])." ".$lang['struct_for']." ".$result[$i]['tbl_name']."\r\n"; + echo "-- ".ucfirst($result[$i]['type'])." ".$lang['struct_for']." ".$result[$i]['tbl_name'].$crlf; else // index or trigger - echo "-- ".$lang['struct_for']." ".$result[$i]['type']." ".$result[$i]['name']." ".$lang['on_tbl']." ".$result[$i]['tbl_name']."\r\n"; - echo "----\r\n"; + echo "-- ".$lang['struct_for']." ".$result[$i]['type']." ".$result[$i]['name']." ".$lang['on_tbl']." ".$result[$i]['tbl_name'].$crlf; + echo "----".$crlf; } - echo $result[$i]['sql'].";\r\n"; + echo $result[$i]['sql'].";".$crlf; } if($data && $result[$i]['type']=="table") { @@ -5608,12 +5869,11 @@ public function export_sql($tables, $drop, $structure, $data, $transaction, $com if($comments) { $numRows = $this->numRows($result[$i]['tbl_name']); - echo "\r\n----\r\n"; - echo "-- ".$lang['data_dump']." ".$result[$i]['tbl_name'].", ".sprintf($lang['total_rows'], $numRows)."\r\n"; - echo "----\r\n"; + echo "\r\n----".$crlf; + echo "-- ".$lang['data_dump']." ".$result[$i]['tbl_name'].", ".sprintf($lang['total_rows'], $numRows).$crlf; + echo "----".$crlf; } - $query = "PRAGMA table_info(".$this->quote_id($result[$i]['tbl_name']).")"; - $temp = $this->selectArray($query); + $temp = $this->getTableInfo($result[$i]['tbl_name']); $cols = array(); $cols_quoted = array(); for($z=0; $zquote($row[$cols[$y]]); } - echo "INSERT INTO ".$this->quote_id($result[$i]['tbl_name'])." (".implode(",", $cols_quoted).") VALUES (".implode(",", $vals).");\r\n"; + echo "INSERT INTO ".$this->quote_id($result[$i]['tbl_name'])." (".implode(",", $cols_quoted).") VALUES (".implode(",", $vals).");".$crlf; } } } } if($transaction) - echo "COMMIT;\r\n"; + echo "COMMIT;".$crlf; } } -// class MicroTimer (issue #146) +class GetParameters +{ + private $_fields; + + public function __construct(array $defaults = array()) + { + $this->_fields = $defaults; + } + + public function __set($key, $value) + { + $this->_fields[$key] = $value; + } + + public function __isset($key) + { + return isset($this->_fields[$key]); + } + + public function __unset($key) + { + unset($this->_fields[$key]); + } + + public function __get($key) + { + return $this->_fields[$key]; + } + + public function getURL(array $assoc = array(), $html = true, $prefix='?') + { + $arg_sep = ($html?'&':'&'); + return $prefix . http_build_query(array_merge($this->_fields, $assoc), '', $arg_sep); + } + + public function getLink(array $assoc = array(), $content = '[ link ]', $class = '', $title = '', $target='') + { + return '' . $content . ''; + } + + public function getForm(array $assoc = array(), $method = 'post', $upload = false, $name = '', $csrf = true) + { + $hidden = ''; + if($method == 'get') + { + $url = ''; + foreach(array_merge($this->_fields, $assoc) as $key => $value) + { + if(!is_null($value)) + $hidden .= ' '; + } + } + else + $url = $this->getURL($assoc); + + if($csrf && $method == 'post') + $hidden .= ''; + + return "
" . + $hidden; + } + + public function redirect(array $assoc = array(), $message="") + { + if($message!="") + { + $_SESSION[COOKIENAME.'messages'][md5($message)] = $message; + $url = $this->getURL(array_merge($assoc, array('message'=>md5($message))), false); + } + else + $url = $this->getURL($assoc, false); + + $protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off' ? 'https' : 'http'); + + header("Location: ".$protocol."://".$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].$url, true, 302); + exit; + } +}// class MicroTimer (issue #146) // wraps calls to microtime(), calculating the elapsed time and rounding output // class MicroTimer { @@ -5749,7 +6092,7 @@ public static function output($resource) // returns data from internal resources, available in single-file mode function getInternalResource($res) { - $resources = array('resources/phpliteadmin.css'=>array(0=>0,1=>4047,),'resources/phpliteadmin.js'=>array(0=>4047,1=>4169,),'resources/favicon.ico'=>array(0=>8216,1=>1448,),); + $resources = array('resources/phpliteadmin.css'=>array(0=>0,1=>4059,),'resources/phpliteadmin.js'=>array(0=>4059,1=>4542,),'resources/favicon.ico'=>array(0=>8601,1=>1448,),); if (isset($resources[$res]) && $f = fopen(__FILE__, 'r')) { fseek($f, __COMPILER_HALT_OFFSET__ + $resources[$res][0]); @@ -5761,7 +6104,7 @@ function getInternalResource($res) { } // resources embedded below, do not edit! -__halt_compiler() ?>body{margin:0px;padding:0px;font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#000;background-color:#e0ebf6;overflow:auto}.body_tbl td{padding:9px 2px 9px 9px}.left_td{width:100px}a{color:#03F;text-decoration:none;cursor:pointer}a:hover{color:#06F}hr{height:1px;border:0;color:#bbb;background-color:#bbb;width:100%}h1{margin:0px;padding:5px;font-size:24px;background-color:#f3cece;text-align:center;color:#000;border-top-left-radius:5px;border-top-right-radius:5px;-moz-border-radius-topleft:5px;-moz-border-radius-topright:5px}#headerlinks{text-align:center;margin-bottom:10px;padding:5px 15px;border-color:#03F;border-width:1px;border-style:solid;border-left-style:none;border-right-style:none;font-size:12px;background-color:#e0ebf6;font-weight:bold}h1 #version{color:#000;font-size:16px}h1 #logo{color:#000}h2{margin:0px;padding:0px;font-size:14px;margin-bottom:20px}input,select,textarea{font-family:Arial,Helvetica,sans-serif;background-color:#eaeaea;color:#03F;border-color:#03F;border-style:solid;border-width:1px;margin:5px;border-radius:5px;-moz-border-radius:5px;padding:3px}input.btn{cursor:pointer}input.btn:hover{background-color:#ccc}fieldset label{min-width:200px;display:block;float:left}fieldset{padding:15px;border-color:#03F;border-width:1px;border-style:solid;border-radius:5px;-moz-border-radius:5px;background-color:#f9f9f9}#container{padding:10px}#leftNav{min-width:250px;padding:0px;border-color:#03F;border-width:1px;border-style:solid;background-color:#FFF;padding-bottom:15px;border-radius:5px;-moz-border-radius:5px}.databaseList select{max-width:200px}.viewTable tr td{padding:1px}#loginBox{width:500px;margin-left:auto;margin-right:auto;margin-top:50px;border-color:#03F;border-width:1px;border-style:solid;background-color:#FFF;border-radius:5px;-moz-border-radius:5px}#main{border-color:#03F;border-width:1px;border-style:solid;padding:15px;background-color:#FFF;border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-top-right-radius:5px;-moz-border-radius-bottomleft:5px;-moz-border-radius-bottomright:5px;-moz-border-radius-topright:5px}.td1{background-color:#f9e3e3;text-align:right;font-size:12px;padding-left:10px;padding-right:10px}.td2{background-color:#f3cece;text-align:right;font-size:12px;padding-left:10px;padding-right:10px}.tdheader{border-color:#03F;border-width:1px;border-style:solid;font-weight:bold;font-size:12px;padding-left:10px;padding-right:10px;background-color:#e0ebf6;border-radius:5px;-moz-border-radius:5px}.confirm{border-color:#03F;border-width:1px;border-style:dashed;padding:15px;background-color:#e0ebf6}.tab{display:block;padding:5px;padding-right:8px;padding-left:8px;border-color:#03F;border-width:1px;border-style:solid;margin-right:5px;float:left;border-bottom-style:none;position:relative;top:1px;padding-bottom:4px;background-color:#eaeaea;border-top-left-radius:5px;border-top-right-radius:5px;-moz-border-radius-topleft:5px;-moz-border-radius-topright:5px}.tab_pressed{display:block;padding:5px;padding-right:8px;padding-left:8px;border-color:#03F;border-width:1px;border-style:solid;margin-right:5px;float:left;border-bottom-style:none;position:relative;top:1px;background-color:#FFF;cursor:default;border-top-left-radius:5px;border-top-right-radius:5px;-moz-border-radius-topleft:5px;-moz-border-radius-topright:5px}.helpq{font-size:11px;font-weight:normal}#help_container{padding:0px;font-size:12px;margin-left:auto;margin-right:auto;background-color:#fff}.help_outer{background-color:#FFF;padding:0px;height:300px;position:relative}.help_list{padding:10px;height:auto}.headd{font-size:14px;font-weight:bold;display:block;padding:10px;background-color:#e0ebf6;border-color:#03F;border-width:1px;border-style:solid;border-left-style:none;border-right-style:none}.help_inner{padding:10px}.help_top{display:block;position:absolute;right:10px;bottom:10px}.warning,.delete,.empty,.drop,.delete_db{color:red}.sidebar_table{font-size:11px}.active_table,.active_db{text-decoration:underline}.null{color:#888}.found{background:#FF0;text-decoration:none} +__halt_compiler() ?>body{margin:0px;padding:0px;font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#000;background-color:#e0ebf6;overflow:auto}.body_tbl td{padding:9px 2px 9px 9px}.left_td{width:100px}a{color:#03F;text-decoration:none;cursor:pointer}a:hover{color:#06F}hr{height:1px;border:0;color:#bbb;background-color:#bbb;width:100%}h1{margin:0px;padding:5px;font-size:24px;background-color:#f3cece;text-align:center;color:#000;border-top-left-radius:5px;border-top-right-radius:5px;-moz-border-radius-topleft:5px;-moz-border-radius-topright:5px}#headerlinks{text-align:center;margin-bottom:10px;padding:5px 15px;border-color:#03F;border-width:1px;border-style:solid;border-left-style:none;border-right-style:none;font-size:12px;background-color:#e0ebf6;font-weight:bold}h1 #version{color:#000;font-size:16px}h1 #logo{color:#000}h2{margin:0px;padding:0px;font-size:14px;margin-bottom:20px}input,select,textarea,.CodeMirror{font-family:Arial,Helvetica,sans-serif;background-color:#eaeaea;color:#03F;border-color:#03F;border-style:solid;border-width:1px;margin:5px;border-radius:5px;-moz-border-radius:5px;padding:3px}input.btn{cursor:pointer}input.btn:hover{background-color:#ccc}fieldset label{min-width:200px;display:block;float:left}fieldset{padding:15px;border-color:#03F;border-width:1px;border-style:solid;border-radius:5px;-moz-border-radius:5px;background-color:#f9f9f9}#container{padding:10px}#leftNav{min-width:250px;padding:0px;border-color:#03F;border-width:1px;border-style:solid;background-color:#FFF;padding-bottom:15px;border-radius:5px;-moz-border-radius:5px}.databaseList select{max-width:200px}.viewTable tr td{padding:1px}#loginBox{width:500px;margin-left:auto;margin-right:auto;margin-top:50px;border-color:#03F;border-width:1px;border-style:solid;background-color:#FFF;border-radius:5px;-moz-border-radius:5px}#main{border-color:#03F;border-width:1px;border-style:solid;padding:15px;background-color:#FFF;border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-top-right-radius:5px;-moz-border-radius-bottomleft:5px;-moz-border-radius-bottomright:5px;-moz-border-radius-topright:5px}.td1{background-color:#f9e3e3;text-align:right;font-size:12px;padding-left:10px;padding-right:10px}.td2{background-color:#f3cece;text-align:right;font-size:12px;padding-left:10px;padding-right:10px}.tdheader{border-color:#03F;border-width:1px;border-style:solid;font-weight:bold;font-size:12px;padding-left:10px;padding-right:10px;background-color:#e0ebf6;border-radius:5px;-moz-border-radius:5px}.confirm{border-color:#03F;border-width:1px;border-style:dashed;padding:15px;background-color:#e0ebf6}.tab{display:block;padding:5px;padding-right:8px;padding-left:8px;border-color:#03F;border-width:1px;border-style:solid;margin-right:5px;float:left;border-bottom-style:none;position:relative;top:1px;padding-bottom:4px;background-color:#eaeaea;border-top-left-radius:5px;border-top-right-radius:5px;-moz-border-radius-topleft:5px;-moz-border-radius-topright:5px}.tab_pressed{display:block;padding:5px;padding-right:8px;padding-left:8px;border-color:#03F;border-width:1px;border-style:solid;margin-right:5px;float:left;border-bottom-style:none;position:relative;top:1px;background-color:#FFF;cursor:default;border-top-left-radius:5px;border-top-right-radius:5px;-moz-border-radius-topleft:5px;-moz-border-radius-topright:5px}.helpq{font-size:11px;font-weight:normal}#help_container{padding:0px;font-size:12px;margin-left:auto;margin-right:auto;background-color:#fff}.help_outer{background-color:#FFF;padding:0px;height:300px;position:relative}.help_list{padding:10px;height:auto}.headd{font-size:14px;font-weight:bold;display:block;padding:10px;background-color:#e0ebf6;border-color:#03F;border-width:1px;border-style:solid;border-left-style:none;border-right-style:none}.help_inner{padding:10px}.help_top{display:block;position:absolute;right:10px;bottom:10px}.warning,.delete,.empty,.drop,.delete_db{color:red}.sidebar_table{font-size:11px}.active_table,.active_db{text-decoration:underline}.null{color:#888}.found{background:#FF0;text-decoration:none} function initAutoincrement() {var i=0;while(document.getElementById('i'+i+'_autoincrement')!=undefined) {document.getElementById('i'+i+'_autoincrement').disabled=true;i++;}} @@ -5789,16 +6132,8 @@ function moveFields() {var fields=document.getElementById("fieldcontainer");var selected=[];for(var i=0;i0){CodeMirror.commands.autocomplete(instance);}} +function checkFileSize(input) +{if(input.files&&input.files.length==1) +{if(input.files[0].size>fileUploadMaxSize) +{alert(fileUploadMaxSizeErrorMsg+": "+(fileUploadMaxSize/1024/1024)+" MiB");return false;}} +return true;}AAABAAEAEBAAAAEAIAAoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwoKZQAAABMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEMDJMAAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASDg7BAAAASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAETDw9CCQkJ1QUFBb4AAABjAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgsLVgAAAO8YExP/AAAA7QAAALEAAAARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQNDUsAAADJGBMT/xgTE/8AAAD/AAAAuAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZERE8DQoKwhgTE/8QDQ2sGBMT/xgTE/8AAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwICHkAAAD/EQwMzQAAAMIAAAD/AAAA7gAAAGEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOCwtWAAAA8RgTE/8IBQW1AAAA/wAAAP8AAADlAAAAHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0KCsEAAAD/EQ8PzAAAAMkAAAD/AAAA/wAAAHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDAAAA/xgTE/8TDw/FAAAA8gAAAP8AAADqAAAAJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAALUAAAD/GBMT/wAAALkAAAD/AAAA/wAAAIkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAAA4wAAAP8GBgbFAAAA2QAAAP8AAADGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF4AAAD5AAAA/RgTE/8AAAD/AAAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAOIAAAD/AAAA/wAAAHYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjAAAArgAAAG4AAAAAAAAAAAAAAAAAAAAA