Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.h2.server.web.WebApp Maven / Gradle / Ivy
/*
* Copyright 2004-2022 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (https://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.server.web;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import org.h2.api.ErrorCode;
import org.h2.bnf.Bnf;
import org.h2.bnf.context.DbColumn;
import org.h2.bnf.context.DbContents;
import org.h2.bnf.context.DbSchema;
import org.h2.bnf.context.DbTableOrView;
import org.h2.command.Parser;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.jdbc.JdbcException;
import org.h2.message.DbException;
import org.h2.security.SHA256;
import org.h2.tools.Backup;
import org.h2.tools.ChangeFileEncryption;
import org.h2.tools.ConvertTraceFile;
import org.h2.tools.CreateCluster;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Recover;
import org.h2.tools.Restore;
import org.h2.tools.RunScript;
import org.h2.tools.Script;
import org.h2.tools.SimpleResultSet;
import org.h2.util.JdbcUtils;
import org.h2.util.NetUtils;
import org.h2.util.NetworkConnectionInfo;
import org.h2.util.Profiler;
import org.h2.util.ScriptReader;
import org.h2.util.SortedProperties;
import org.h2.util.StringUtils;
import org.h2.util.Tool;
import org.h2.util.Utils;
import org.h2.util.Utils10;
import org.h2.value.DataType;
/**
* For each connection to a session, an object of this class is created.
* This class is used by the H2 Console.
*/
public class WebApp {
private static final Comparator SYSTEM_SCHEMA_COMPARATOR = Comparator
.comparing(DbTableOrView::getName, String.CASE_INSENSITIVE_ORDER);
/**
* The web server.
*/
protected final WebServer server;
/**
* The session.
*/
protected WebSession session;
/**
* The session attributes
*/
protected Properties attributes;
/**
* The mime type of the current response.
*/
protected String mimeType;
/**
* Whether the response can be cached.
*/
protected boolean cache;
/**
* Whether to close the connection.
*/
protected boolean stop;
/**
* The language in the HTTP header.
*/
protected String headerLanguage;
private Profiler profiler;
WebApp(WebServer server) {
this.server = server;
}
/**
* Set the web session and attributes.
*
* @param session the session
* @param attributes the attributes
*/
void setSession(WebSession session, Properties attributes) {
this.session = session;
this.attributes = attributes;
}
/**
* Process an HTTP request.
*
* @param file the file that was requested
* @param networkConnectionInfo the network connection information
* @return the name of the file to return to the client
*/
String processRequest(String file, NetworkConnectionInfo networkConnectionInfo) {
int index = file.lastIndexOf('.');
String suffix;
if (index >= 0) {
suffix = file.substring(index + 1);
} else {
suffix = "";
}
if ("ico".equals(suffix)) {
mimeType = "image/x-icon";
cache = true;
} else if ("gif".equals(suffix)) {
mimeType = "image/gif";
cache = true;
} else if ("css".equals(suffix)) {
cache = true;
mimeType = "text/css";
} else if ("html".equals(suffix) ||
"do".equals(suffix) ||
"jsp".equals(suffix)) {
cache = false;
mimeType = "text/html";
if (session == null) {
session = server.createNewSession(
NetUtils.ipToShortForm(null, networkConnectionInfo.getClientAddr(), false).toString());
if (!"notAllowed.jsp".equals(file)) {
file = "index.do";
}
}
} else if ("js".equals(suffix)) {
cache = true;
mimeType = "text/javascript";
} else {
cache = true;
mimeType = "application/octet-stream";
}
trace("mimeType=" + mimeType);
trace(file);
if (file.endsWith(".do")) {
file = process(file, networkConnectionInfo);
} else if (file.endsWith(".jsp")) {
switch (file) {
case "admin.jsp":
case "tools.jsp":
if (!checkAdmin(file)) {
file = process("adminLogin.do", networkConnectionInfo);
}
}
}
return file;
}
private static String getComboBox(String[] elements, String selected) {
StringBuilder buff = new StringBuilder();
for (String value : elements) {
buff.append("').
append(PageParser.escapeHtml(value)).
append(" ");
}
return buff.toString();
}
private static String getComboBox(String[][] elements, String selected) {
StringBuilder buff = new StringBuilder();
for (String[] n : elements) {
buff.append("').
append(PageParser.escapeHtml(n[1])).
append(" ");
}
return buff.toString();
}
private String process(String file, NetworkConnectionInfo networkConnectionInfo) {
trace("process " + file);
while (file.endsWith(".do")) {
switch (file) {
case "login.do":
file = login(networkConnectionInfo);
break;
case "index.do":
file = index();
break;
case "logout.do":
file = logout();
break;
case "settingRemove.do":
file = settingRemove();
break;
case "settingSave.do":
file = settingSave();
break;
case "test.do":
file = test(networkConnectionInfo);
break;
case "query.do":
file = query();
break;
case "tables.do":
file = tables();
break;
case "editResult.do":
file = editResult();
break;
case "getHistory.do":
file = getHistory();
break;
case "admin.do":
file = checkAdmin(file) ? admin() : "adminLogin.do";
break;
case "adminSave.do":
file = checkAdmin(file) ? adminSave() : "adminLogin.do";
break;
case "adminStartTranslate.do":
file = checkAdmin(file) ? adminStartTranslate() : "adminLogin.do";
break;
case "adminShutdown.do":
file = checkAdmin(file) ? adminShutdown() : "adminLogin.do";
break;
case "autoCompleteList.do":
file = autoCompleteList();
break;
case "tools.do":
file = checkAdmin(file) ? tools() : "adminLogin.do";
break;
case "adminLogin.do":
file = adminLogin();
break;
default:
file = "error.jsp";
break;
}
}
trace("return " + file);
return file;
}
private boolean checkAdmin(String file) {
Boolean b = (Boolean) session.get("admin");
if (b != null && b) {
return true;
}
String key = server.getKey();
if (key != null && key.equals(session.get("key"))) {
return true;
}
session.put("adminBack", file);
return false;
}
private String adminLogin() {
String password = attributes.getProperty("password");
if (password == null || password.isEmpty() || !server.checkAdminPassword(password)) {
return "adminLogin.jsp";
}
String back = (String) session.remove("adminBack");
session.put("admin", true);
return back != null ? back : "admin.do";
}
private String autoCompleteList() {
String query = (String) attributes.get("query");
boolean lowercase = false;
String tQuery = query.trim();
if (!tQuery.isEmpty() && Character.isLowerCase(tQuery.charAt(0))) {
lowercase = true;
}
try {
String sql = query;
if (sql.endsWith(";")) {
sql += " ";
}
ScriptReader reader = new ScriptReader(new StringReader(sql));
reader.setSkipRemarks(true);
String lastSql = "";
while (true) {
String n = reader.readStatement();
if (n == null) {
break;
}
lastSql = n;
}
String result = "";
if (reader.isInsideRemark()) {
if (reader.isBlockRemark()) {
result = "1#(End Remark)# */\n" + result;
} else {
result = "1#(Newline)#\n" + result;
}
} else {
sql = lastSql;
while (sql.length() > 0 && sql.charAt(0) <= ' ') {
sql = sql.substring(1);
}
String tSql = sql.trim();
if (!tSql.isEmpty() && Character.isLowerCase(tSql.charAt(0))) {
lowercase = true;
}
Bnf bnf = session.getBnf();
if (bnf == null) {
return "autoCompleteList.jsp";
}
HashMap map = bnf.getNextTokenList(sql);
String space = "";
if (sql.length() > 0) {
char last = sql.charAt(sql.length() - 1);
if (!Character.isWhitespace(last) && (last != '.' &&
last >= ' ' && last != '\'' && last != '"')) {
space = " ";
}
}
ArrayList list = new ArrayList<>(map.size());
for (Map.Entry entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
String type = String.valueOf(key.charAt(0));
if (Integer.parseInt(type) > 2) {
continue;
}
key = key.substring(2);
if (Character.isLetter(key.charAt(0)) && lowercase) {
key = StringUtils.toLowerEnglish(key);
value = StringUtils.toLowerEnglish(value);
}
if (key.equals(value) && !".".equals(value)) {
value = space + value;
}
key = StringUtils.urlEncode(key);
key = key.replace('+', ' ');
value = StringUtils.urlEncode(value);
value = value.replace('+', ' ');
list.add(type + "#" + key + "#" + value);
}
Collections.sort(list);
if (query.endsWith("\n") || tQuery.endsWith(";")) {
list.add(0, "1#(Newline)#\n");
}
result = StringUtils.join(new StringBuilder(), list, "|").toString();
}
session.put("autoCompleteList", result);
} catch (Throwable e) {
server.traceError(e);
}
return "autoCompleteList.jsp";
}
private String admin() {
session.put("port", Integer.toString(server.getPort()));
session.put("allowOthers", Boolean.toString(server.getAllowOthers()));
session.put("ssl", String.valueOf(server.getSSL()));
session.put("sessions", server.getSessions());
return "admin.jsp";
}
private String adminSave() {
try {
Properties prop = new SortedProperties();
int port = Integer.decode((String) attributes.get("port"));
prop.setProperty("webPort", Integer.toString(port));
server.setPort(port);
boolean allowOthers = Utils.parseBoolean((String) attributes.get("allowOthers"), false, false);
prop.setProperty("webAllowOthers", String.valueOf(allowOthers));
server.setAllowOthers(allowOthers);
boolean ssl = Utils.parseBoolean((String) attributes.get("ssl"), false, false);
prop.setProperty("webSSL", String.valueOf(ssl));
server.setSSL(ssl);
byte[] adminPassword = server.getAdminPassword();
if (adminPassword != null) {
prop.setProperty("webAdminPassword", StringUtils.convertBytesToHex(adminPassword));
}
server.saveProperties(prop);
} catch (Exception e) {
trace(e.toString());
}
return admin();
}
private String tools() {
try {
String toolName = (String) attributes.get("tool");
session.put("tool", toolName);
String args = (String) attributes.get("args");
String[] argList = StringUtils.arraySplit(args, ',', false);
Tool tool = null;
if ("Backup".equals(toolName)) {
tool = new Backup();
} else if ("Restore".equals(toolName)) {
tool = new Restore();
} else if ("Recover".equals(toolName)) {
tool = new Recover();
} else if ("DeleteDbFiles".equals(toolName)) {
tool = new DeleteDbFiles();
} else if ("ChangeFileEncryption".equals(toolName)) {
tool = new ChangeFileEncryption();
} else if ("Script".equals(toolName)) {
tool = new Script();
} else if ("RunScript".equals(toolName)) {
tool = new RunScript();
} else if ("ConvertTraceFile".equals(toolName)) {
tool = new ConvertTraceFile();
} else if ("CreateCluster".equals(toolName)) {
tool = new CreateCluster();
} else {
throw DbException.getInternalError(toolName);
}
ByteArrayOutputStream outBuff = new ByteArrayOutputStream();
PrintStream out = new PrintStream(outBuff, false, "UTF-8");
tool.setOut(out);
try {
tool.runTool(argList);
out.flush();
String o = Utils10.byteArrayOutputStreamToString(outBuff, StandardCharsets.UTF_8);
String result = PageParser.escapeHtml(o);
session.put("toolResult", result);
} catch (Exception e) {
session.put("toolResult", getStackTrace(0, e, true));
}
} catch (Exception e) {
server.traceError(e);
}
return "tools.jsp";
}
private String adminStartTranslate() {
Map, ?> p = Map.class.cast(session.map.get("text"));
@SuppressWarnings("unchecked")
Map p2 = (Map) p;
String file = server.startTranslate(p2);
session.put("translationFile", file);
return "helpTranslate.jsp";
}
/**
* Stop the application and the server.
*
* @return the page to display
*/
protected String adminShutdown() {
server.shutdown();
return "admin.jsp";
}
private String index() {
String[][] languageArray = WebServer.LANGUAGES;
String language = (String) attributes.get("language");
Locale locale = session.locale;
if (language != null) {
if (locale == null || !StringUtils.toLowerEnglish(
locale.getLanguage()).equals(language)) {
locale = new Locale(language, "");
server.readTranslations(session, locale.getLanguage());
session.put("language", language);
session.locale = locale;
}
} else {
language = (String) session.get("language");
}
if (language == null) {
// if the language is not yet known
// use the last header
language = headerLanguage;
}
session.put("languageCombo", getComboBox(languageArray, language));
String[] settingNames = server.getSettingNames();
String setting = attributes.getProperty("setting");
if (setting == null && settingNames.length > 0) {
setting = settingNames[0];
}
String combobox = getComboBox(settingNames, setting);
session.put("settingsList", combobox);
ConnectionInfo info = server.getSetting(setting);
if (info == null) {
info = new ConnectionInfo();
}
session.put("setting", PageParser.escapeHtmlData(setting));
session.put("name", PageParser.escapeHtmlData(setting));
session.put("driver", PageParser.escapeHtmlData(info.driver));
session.put("url", PageParser.escapeHtmlData(info.url));
session.put("user", PageParser.escapeHtmlData(info.user));
return "index.jsp";
}
private String getHistory() {
int id = Integer.parseInt(attributes.getProperty("id"));
String sql = session.getCommand(id);
session.put("query", PageParser.escapeHtmlData(sql));
return "query.jsp";
}
private static int addColumns(boolean mainSchema, DbTableOrView table, StringBuilder builder, int treeIndex,
boolean showColumnTypes, StringBuilder columnsBuilder) {
DbColumn[] columns = table.getColumns();
for (int i = 0; columns != null && i < columns.length; i++) {
DbColumn column = columns[i];
if (columnsBuilder.length() > 0) {
columnsBuilder.append(' ');
}
columnsBuilder.append(column.getName());
String col = escapeIdentifier(column.getName());
String level = mainSchema ? ", 1, 1" : ", 2, 2";
builder.append("setNode(").append(treeIndex).append(level)
.append(", 'column', '")
.append(PageParser.escapeJavaScript(column.getName()))
.append("', 'javascript:ins(\\'").append(col).append("\\')');\n");
treeIndex++;
if (mainSchema && showColumnTypes) {
builder.append("setNode(").append(treeIndex)
.append(", 2, 2, 'type', '")
.append(PageParser.escapeJavaScript(column.getDataType()))
.append("', null);\n");
treeIndex++;
}
}
return treeIndex;
}
private static String escapeIdentifier(String name) {
return StringUtils.urlEncode(
PageParser.escapeJavaScript(name)).replace('+', ' ');
}
/**
* This class represents index information for the GUI.
*/
static class IndexInfo {
/**
* The index name.
*/
String name;
/**
* The index type name.
*/
String type;
/**
* The indexed columns.
*/
String columns;
}
private static int addIndexes(boolean mainSchema, DatabaseMetaData meta,
String table, String schema, StringBuilder buff, int treeIndex)
throws SQLException {
ResultSet rs;
try {
rs = meta.getIndexInfo(null, schema, table, false, true);
} catch (SQLException e) {
// SQLite
return treeIndex;
}
HashMap indexMap = new HashMap<>();
while (rs.next()) {
String name = rs.getString("INDEX_NAME");
IndexInfo info = indexMap.get(name);
if (info == null) {
int t = rs.getInt("TYPE");
String type;
if (t == DatabaseMetaData.tableIndexClustered) {
type = "";
} else if (t == DatabaseMetaData.tableIndexHashed) {
type = " (${text.tree.hashed})";
} else if (t == DatabaseMetaData.tableIndexOther) {
type = "";
} else {
type = null;
}
if (name != null && type != null) {
info = new IndexInfo();
info.name = name;
type = (rs.getBoolean("NON_UNIQUE") ?
"${text.tree.nonUnique}" : "${text.tree.unique}") + type;
info.type = type;
info.columns = rs.getString("COLUMN_NAME");
indexMap.put(name, info);
}
} else {
info.columns += ", " + rs.getString("COLUMN_NAME");
}
}
rs.close();
if (indexMap.size() > 0) {
String level = mainSchema ? ", 1, 1" : ", 2, 1";
String levelIndex = mainSchema ? ", 2, 1" : ", 3, 1";
String levelColumnType = mainSchema ? ", 3, 2" : ", 4, 2";
buff.append("setNode(").append(treeIndex).append(level)
.append(", 'index_az', '${text.tree.indexes}', null);\n");
treeIndex++;
for (IndexInfo info : indexMap.values()) {
buff.append("setNode(").append(treeIndex).append(levelIndex)
.append(", 'index', '")
.append(PageParser.escapeJavaScript(info.name))
.append("', null);\n");
treeIndex++;
buff.append("setNode(").append(treeIndex).append(levelColumnType)
.append(", 'type', '").append(info.type).append("', null);\n");
treeIndex++;
buff.append("setNode(").append(treeIndex).append(levelColumnType)
.append(", 'type', '")
.append(PageParser.escapeJavaScript(info.columns))
.append("', null);\n");
treeIndex++;
}
}
return treeIndex;
}
private int addTablesAndViews(DbSchema schema, boolean mainSchema, StringBuilder builder, int treeIndex)
throws SQLException {
if (schema == null) {
return treeIndex;
}
Connection conn = session.getConnection();
DatabaseMetaData meta = session.getMetaData();
int level = mainSchema ? 0 : 1;
boolean showColumns = mainSchema || !schema.isSystem;
String indentation = ", " + level + ", " + (showColumns ? "1" : "2") + ", ";
String indentNode = ", " + (level + 1) + ", 2, ";
DbTableOrView[] tables = schema.getTables();
if (tables == null) {
return treeIndex;
}
DbContents contents = schema.getContents();
boolean isOracle = contents.isOracle();
boolean notManyTables = tables.length < SysProperties.CONSOLE_MAX_TABLES_LIST_INDEXES;
try (PreparedStatement prep = showColumns ? prepareViewDefinitionQuery(conn, contents) : null) {
if (prep != null) {
prep.setString(1, schema.name);
}
if (schema.isSystem) {
Arrays.sort(tables, SYSTEM_SCHEMA_COMPARATOR);
for (DbTableOrView table : tables) {
treeIndex = addTableOrView(schema, mainSchema, builder, treeIndex, meta, false, indentation,
isOracle, notManyTables, table, table.isView(), prep, indentNode);
}
} else {
for (DbTableOrView table : tables) {
if (table.isView()) {
continue;
}
treeIndex = addTableOrView(schema, mainSchema, builder, treeIndex, meta, showColumns, indentation,
isOracle, notManyTables, table, false, null, indentNode);
}
for (DbTableOrView table : tables) {
if (!table.isView()) {
continue;
}
treeIndex = addTableOrView(schema, mainSchema, builder, treeIndex, meta, showColumns, indentation,
isOracle, notManyTables, table, true, prep, indentNode);
}
}
}
return treeIndex;
}
private static PreparedStatement prepareViewDefinitionQuery(Connection conn, DbContents contents) {
if (contents.mayHaveStandardViews()) {
try {
return conn.prepareStatement("SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS"
+ " WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?");
} catch (SQLException e) {
contents.setMayHaveStandardViews(false);
}
}
return null;
}
private static int addTableOrView(DbSchema schema, boolean mainSchema, StringBuilder builder, int treeIndex,
DatabaseMetaData meta, boolean showColumns, String indentation, boolean isOracle, boolean notManyTables,
DbTableOrView table, boolean isView, PreparedStatement prep, String indentNode) throws SQLException {
int tableId = treeIndex;
String tab = table.getQuotedName();
if (!mainSchema) {
tab = schema.quotedName + '.' + tab;
}
tab = escapeIdentifier(tab);
builder.append("setNode(").append(treeIndex).append(indentation)
.append(" '").append(isView ? "view" : "table").append("', '")
.append(PageParser.escapeJavaScript(table.getName()))
.append("', 'javascript:ins(\\'").append(tab).append("\\',true)');\n");
treeIndex++;
if (showColumns) {
StringBuilder columnsBuilder = new StringBuilder();
treeIndex = addColumns(mainSchema, table, builder, treeIndex, notManyTables, columnsBuilder);
if (isView) {
if (prep != null) {
prep.setString(2, table.getName());
try (ResultSet rs = prep.executeQuery()) {
if (rs.next()) {
String sql = rs.getString(1);
if (sql != null) {
builder.append("setNode(").append(treeIndex).append(indentNode).append(" 'type', '")
.append(PageParser.escapeJavaScript(sql)).append("', null);\n");
treeIndex++;
}
}
}
}
} else if (!isOracle && notManyTables) {
treeIndex = addIndexes(mainSchema, meta, table.getName(), schema.name, builder, treeIndex);
}
builder.append("addTable('")
.append(PageParser.escapeJavaScript(table.getName())).append("', '")
.append(PageParser.escapeJavaScript(columnsBuilder.toString())).append("', ")
.append(tableId).append(");\n");
}
return treeIndex;
}
private String tables() {
DbContents contents = session.getContents();
boolean isH2 = false;
try {
String url = (String) session.get("url");
Connection conn = session.getConnection();
contents.readContents(url, conn);
session.loadBnf();
isH2 = contents.isH2();
StringBuilder buff = new StringBuilder()
.append("setNode(0, 0, 0, 'database', '")
.append(PageParser.escapeJavaScript(url))
.append("', null);\n");
int treeIndex = 1;
DbSchema defaultSchema = contents.getDefaultSchema();
treeIndex = addTablesAndViews(defaultSchema, true, buff, treeIndex);
DbSchema[] schemas = contents.getSchemas();
for (DbSchema schema : schemas) {
if (schema == defaultSchema || schema == null) {
continue;
}
buff.append("setNode(").append(treeIndex).append(", 0, 1, 'folder', '")
.append(PageParser.escapeJavaScript(schema.name))
.append("', null);\n");
treeIndex++;
treeIndex = addTablesAndViews(schema, false, buff, treeIndex);
}
if (isH2) {
try (Statement stat = conn.createStatement()) {
ResultSet rs;
try {
rs = stat.executeQuery("SELECT SEQUENCE_NAME, BASE_VALUE, INCREMENT FROM " +
"INFORMATION_SCHEMA.SEQUENCES ORDER BY SEQUENCE_NAME");
} catch (SQLException e) {
rs = stat.executeQuery("SELECT SEQUENCE_NAME, CURRENT_VALUE, INCREMENT FROM " +
"INFORMATION_SCHEMA.SEQUENCES ORDER BY SEQUENCE_NAME");
}
for (int i = 0; rs.next(); i++) {
if (i == 0) {
buff.append("setNode(").append(treeIndex)
.append(", 0, 1, 'sequences', '${text.tree.sequences}', null);\n");
treeIndex++;
}
String name = rs.getString(1);
String currentBase = rs.getString(2);
String increment = rs.getString(3);
buff.append("setNode(").append(treeIndex)
.append(", 1, 1, 'sequence', '")
.append(PageParser.escapeJavaScript(name))
.append("', null);\n");
treeIndex++;
buff.append("setNode(").append(treeIndex)
.append(", 2, 2, 'type', '${text.tree.current}: ")
.append(PageParser.escapeJavaScript(currentBase))
.append("', null);\n");
treeIndex++;
if (!"1".equals(increment)) {
buff.append("setNode(").append(treeIndex)
.append(", 2, 2, 'type', '${text.tree.increment}: ")
.append(PageParser.escapeJavaScript(increment))
.append("', null);\n");
treeIndex++;
}
}
rs.close();
try {
rs = stat.executeQuery(
"SELECT USER_NAME, IS_ADMIN FROM INFORMATION_SCHEMA.USERS ORDER BY USER_NAME");
} catch (SQLException e) {
rs = stat.executeQuery("SELECT NAME, ADMIN FROM INFORMATION_SCHEMA.USERS ORDER BY NAME");
}
for (int i = 0; rs.next(); i++) {
if (i == 0) {
buff.append("setNode(").append(treeIndex)
.append(", 0, 1, 'users', '${text.tree.users}', null);\n");
treeIndex++;
}
String name = rs.getString(1);
String admin = rs.getString(2);
buff.append("setNode(").append(treeIndex)
.append(", 1, 1, 'user', '")
.append(PageParser.escapeJavaScript(name))
.append("', null);\n");
treeIndex++;
if (admin.equalsIgnoreCase("TRUE")) {
buff.append("setNode(").append(treeIndex)
.append(", 2, 2, 'type', '${text.tree.admin}', null);\n");
treeIndex++;
}
}
rs.close();
}
}
DatabaseMetaData meta = session.getMetaData();
String version = meta.getDatabaseProductName() + " " +
meta.getDatabaseProductVersion();
buff.append("setNode(").append(treeIndex)
.append(", 0, 0, 'info', '")
.append(PageParser.escapeJavaScript(version))
.append("', null);\n")
.append("refreshQueryTables();");
session.put("tree", buff.toString());
} catch (Exception e) {
session.put("tree", "");
session.put("error", getStackTrace(0, e, isH2));
}
return "tables.jsp";
}
private String getStackTrace(int id, Throwable e, boolean isH2) {
try {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
String stackTrace = writer.toString();
stackTrace = PageParser.escapeHtml(stackTrace);
if (isH2) {
stackTrace = linkToSource(stackTrace);
}
stackTrace = StringUtils.replaceAll(stackTrace, "\t",
" ");
String message = PageParser.escapeHtml(e.getMessage());
String error = "" + message +
" ";
if (e instanceof SQLException) {
SQLException se = (SQLException) e;
error += " " + se.getSQLState() + "/" + se.getErrorCode();
if (isH2) {
int code = se.getErrorCode();
error += " (${text.a.help}) ";
}
}
error += " " + stackTrace + " ";
error = formatAsError(error);
return error;
} catch (OutOfMemoryError e2) {
server.traceError(e);
return e.toString();
}
}
private static String linkToSource(String s) {
try {
StringBuilder result = new StringBuilder(s.length());
int idx = s.indexOf(" ");
result.append(s, 0, idx);
while (true) {
int start = s.indexOf("org.h2.", idx);
if (start < 0) {
result.append(s.substring(idx));
break;
}
result.append(s, idx, start);
int end = s.indexOf(')', start);
if (end < 0) {
result.append(s.substring(idx));
break;
}
String element = s.substring(start, end);
int open = element.lastIndexOf('(');
int dotMethod = element.lastIndexOf('.', open - 1);
int dotClass = element.lastIndexOf('.', dotMethod - 1);
String packageName = element.substring(0, dotClass);
int colon = element.lastIndexOf(':');
String file = element.substring(open + 1, colon);
String lineNumber = element.substring(colon + 1, element.length());
String fullFileName = packageName.replace('.', '/') + "/" + file;
result.append("");
result.append(element);
result.append(" ");
idx = end;
}
return result.toString();
} catch (Throwable t) {
return s;
}
}
private static String formatAsError(String s) {
return "" + s + "
";
}
private String test(NetworkConnectionInfo networkConnectionInfo) {
String driver = attributes.getProperty("driver", "");
String url = attributes.getProperty("url", "");
String user = attributes.getProperty("user", "");
String password = attributes.getProperty("password", "");
session.put("driver", driver);
session.put("url", url);
session.put("user", user);
boolean isH2 = url.startsWith("jdbc:h2:");
try {
long start = System.currentTimeMillis();
String profOpen = "", profClose = "";
Profiler prof = new Profiler();
prof.startCollecting();
Connection conn;
try {
conn = server.getConnection(driver, url, user, password, null, networkConnectionInfo);
} finally {
prof.stopCollecting();
profOpen = prof.getTop(3);
}
prof = new Profiler();
prof.startCollecting();
try {
JdbcUtils.closeSilently(conn);
} finally {
prof.stopCollecting();
profClose = prof.getTop(3);
}
long time = System.currentTimeMillis() - start;
String success;
if (time > 1000) {
success = "" +
"${text.login.testSuccessful} " +
" " +
PageParser.escapeHtml(profOpen) +
" " +
PageParser.escapeHtml(profClose) +
" ";
} else {
success = "${text.login.testSuccessful}
";
}
session.put("error", success);
// session.put("error", "${text.login.testSuccessful}");
return "login.jsp";
} catch (Exception e) {
session.put("error", getLoginError(e, isH2));
return "login.jsp";
}
}
/**
* Get the formatted login error message.
*
* @param e the exception
* @param isH2 if the current database is a H2 database
* @return the formatted error message
*/
private String getLoginError(Exception e, boolean isH2) {
if (e instanceof JdbcException && ((JdbcException) e).getErrorCode() == ErrorCode.CLASS_NOT_FOUND_1) {
return "${text.login.driverNotFound} " + getStackTrace(0, e, isH2);
}
return getStackTrace(0, e, isH2);
}
private String login(NetworkConnectionInfo networkConnectionInfo) {
String driver = attributes.getProperty("driver", "");
String url = attributes.getProperty("url", "");
String user = attributes.getProperty("user", "");
String password = attributes.getProperty("password", "");
session.put("autoCommit", "checked");
session.put("autoComplete", "1");
session.put("maxrows", "1000");
boolean isH2 = url.startsWith("jdbc:h2:");
try {
Connection conn = server.getConnection(driver, url, user, password, (String) session.get("key"),
networkConnectionInfo);
session.setConnection(conn);
session.put("url", url);
session.put("user", user);
session.remove("error");
settingSave();
return "frame.jsp";
} catch (Exception e) {
session.put("error", getLoginError(e, isH2));
return "login.jsp";
}
}
private String logout() {
try {
Connection conn = session.getConnection();
session.setConnection(null);
session.remove("conn");
session.remove("result");
session.remove("tables");
session.remove("user");
session.remove("tool");
if (conn != null) {
if (session.getShutdownServerOnDisconnect()) {
server.shutdown();
} else {
conn.close();
}
}
} catch (Exception e) {
trace(e.toString());
}
session.remove("admin");
return "index.do";
}
private String query() {
String sql = attributes.getProperty("sql").trim();
try {
ScriptReader r = new ScriptReader(new StringReader(sql));
final ArrayList list = new ArrayList<>();
while (true) {
String s = r.readStatement();
if (s == null) {
break;
}
list.add(s);
}
final Connection conn = session.getConnection();
if (SysProperties.CONSOLE_STREAM && server.getAllowChunked()) {
String page = new String(server.getFile("result.jsp"), StandardCharsets.UTF_8);
int idx = page.indexOf("${result}");
// the first element of the list is the header, the last the
// footer
list.add(0, page.substring(0, idx));
list.add(page.substring(idx + "${result}".length()));
session.put("chunks", new Iterator() {
private int i;
@Override
public boolean hasNext() {
return i < list.size();
}
@Override
public String next() {
String s = list.get(i++);
if (i == 1 || i == list.size()) {
return s;
}
StringBuilder b = new StringBuilder();
query(conn, s, i - 1, list.size() - 2, b);
return b.toString();
}
});
return "result.jsp";
}
String result;
StringBuilder buff = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
query(conn, s, i, list.size(), buff);
}
result = buff.toString();
session.put("result", result);
} catch (Throwable e) {
session.put("result", getStackTrace(0, e, session.getContents().isH2()));
}
return "result.jsp";
}
/**
* Execute a query and append the result to the buffer.
*
* @param conn the connection
* @param s the statement
* @param i the index
* @param size the number of statements
* @param buff the target buffer
*/
void query(Connection conn, String s, int i, int size, StringBuilder buff) {
if (!(s.startsWith("@") && s.endsWith("."))) {
buff.append(PageParser.escapeHtml(s + ";")).append(" ");
}
boolean forceEdit = s.startsWith("@edit");
buff.append(getResult(conn, i + 1, s, size == 1, forceEdit)).
append(" ");
}
private String editResult() {
ResultSet rs = session.result;
int row = Integer.parseInt(attributes.getProperty("row"));
int op = Integer.parseInt(attributes.getProperty("op"));
String result = "", error = "";
try {
if (op == 1) {
boolean insert = row < 0;
if (insert) {
rs.moveToInsertRow();
} else {
rs.absolute(row);
}
for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
String x = attributes.getProperty("r" + row + "c" + (i + 1));
unescapeData(x, rs, i + 1);
}
if (insert) {
rs.insertRow();
} else {
rs.updateRow();
}
} else if (op == 2) {
rs.absolute(row);
rs.deleteRow();
} else if (op == 3) {
// cancel
}
} catch (Throwable e) {
result = " " + getStackTrace(0, e, session.getContents().isH2());
error = formatAsError(e.getMessage());
}
String sql = "@edit " + (String) session.get("resultSetSQL");
Connection conn = session.getConnection();
result = error + getResult(conn, -1, sql, true, true) + result;
session.put("result", result);
return "result.jsp";
}
private int getMaxrows() {
String r = (String) session.get("maxrows");
return r == null ? 0 : Integer.parseInt(r);
}
private String getResult(Connection conn, int id, String sql,
boolean allowEdit, boolean forceEdit) {
try {
sql = sql.trim();
StringBuilder buff = new StringBuilder();
String sqlUpper = StringUtils.toUpperEnglish(sql);
if (sqlUpper.contains("CREATE") ||
sqlUpper.contains("DROP") ||
sqlUpper.contains("ALTER") ||
sqlUpper.contains("RUNSCRIPT")) {
String sessionId = attributes.getProperty("jsessionid");
buff.append("");
}
Statement stat;
DbContents contents = session.getContents();
if (forceEdit || (allowEdit && contents.isH2())) {
stat = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
} else {
stat = conn.createStatement();
}
ResultSet rs;
long time = System.currentTimeMillis();
boolean metadata = false;
Object generatedKeys = null;
boolean edit = false;
boolean list = false;
if (JdbcUtils.isBuiltIn(sql, "@autocommit_true")) {
conn.setAutoCommit(true);
return "${text.result.autoCommitOn}";
} else if (JdbcUtils.isBuiltIn(sql, "@autocommit_false")) {
conn.setAutoCommit(false);
return "${text.result.autoCommitOff}";
} else if (JdbcUtils.isBuiltIn(sql, "@cancel")) {
stat = session.executingStatement;
if (stat != null) {
stat.cancel();
buff.append("${text.result.statementWasCanceled}");
} else {
buff.append("${text.result.noRunningStatement}");
}
return buff.toString();
} else if (JdbcUtils.isBuiltIn(sql, "@edit")) {
edit = true;
sql = StringUtils.trimSubstring(sql, "@edit".length());
session.put("resultSetSQL", sql);
}
if (JdbcUtils.isBuiltIn(sql, "@list")) {
list = true;
sql = StringUtils.trimSubstring(sql, "@list".length());
}
if (JdbcUtils.isBuiltIn(sql, "@meta")) {
metadata = true;
sql = StringUtils.trimSubstring(sql, "@meta".length());
}
if (JdbcUtils.isBuiltIn(sql, "@generated")) {
generatedKeys = true;
int offset = "@generated".length();
int length = sql.length();
for (; offset < length; offset++) {
char c = sql.charAt(offset);
if (c == '(') {
Parser p = new Parser();
generatedKeys = p.parseColumnList(sql, offset);
offset = p.getLastParseIndex();
break;
}
if (!Character.isWhitespace(c)) {
break;
}
}
sql = StringUtils.trimSubstring(sql, offset);
} else if (JdbcUtils.isBuiltIn(sql, "@history")) {
buff.append(getCommandHistoryString());
return buff.toString();
} else if (JdbcUtils.isBuiltIn(sql, "@loop")) {
sql = StringUtils.trimSubstring(sql, "@loop".length());
int idx = sql.indexOf(' ');
int count = Integer.decode(sql.substring(0, idx));
sql = StringUtils.trimSubstring(sql, idx);
return executeLoop(conn, count, sql);
} else if (JdbcUtils.isBuiltIn(sql, "@maxrows")) {
int maxrows = (int) Double.parseDouble(StringUtils.trimSubstring(sql, "@maxrows".length()));
session.put("maxrows", Integer.toString(maxrows));
return "${text.result.maxrowsSet}";
} else if (JdbcUtils.isBuiltIn(sql, "@parameter_meta")) {
sql = StringUtils.trimSubstring(sql, "@parameter_meta".length());
PreparedStatement prep = conn.prepareStatement(sql);
buff.append(getParameterResultSet(prep.getParameterMetaData()));
return buff.toString();
} else if (JdbcUtils.isBuiltIn(sql, "@password_hash")) {
sql = StringUtils.trimSubstring(sql, "@password_hash".length());
String[] p = JdbcUtils.split(sql);
return StringUtils.convertBytesToHex(
SHA256.getKeyPasswordHash(p[0], p[1].toCharArray()));
} else if (JdbcUtils.isBuiltIn(sql, "@prof_start")) {
if (profiler != null) {
profiler.stopCollecting();
}
profiler = new Profiler();
profiler.startCollecting();
return "Ok";
} else if (JdbcUtils.isBuiltIn(sql, "@sleep")) {
String s = StringUtils.trimSubstring(sql, "@sleep".length());
int sleep = 1;
if (s.length() > 0) {
sleep = Integer.parseInt(s);
}
Thread.sleep(sleep * 1000);
return "Ok";
} else if (JdbcUtils.isBuiltIn(sql, "@transaction_isolation")) {
String s = StringUtils.trimSubstring(sql, "@transaction_isolation".length());
if (s.length() > 0) {
int level = Integer.parseInt(s);
conn.setTransactionIsolation(level);
}
buff.append("Transaction Isolation: ")
.append(conn.getTransactionIsolation())
.append(" ");
buff.append(Connection.TRANSACTION_READ_UNCOMMITTED)
.append(": read_uncommitted ");
buff.append(Connection.TRANSACTION_READ_COMMITTED)
.append(": read_committed ");
buff.append(Connection.TRANSACTION_REPEATABLE_READ)
.append(": repeatable_read ");
buff.append(Constants.TRANSACTION_SNAPSHOT)
.append(": snapshot ");
buff.append(Connection.TRANSACTION_SERIALIZABLE)
.append(": serializable");
}
if (sql.startsWith("@")) {
rs = JdbcUtils.getMetaResultSet(conn, sql);
if (rs == null && JdbcUtils.isBuiltIn(sql, "@prof_stop")) {
if (profiler != null) {
profiler.stopCollecting();
SimpleResultSet simple = new SimpleResultSet();
simple.addColumn("Top Stack Trace(s)", Types.VARCHAR, 0, 0);
simple.addRow(profiler.getTop(3));
rs = simple;
profiler = null;
}
}
if (rs == null) {
buff.append("?: ").append(sql);
return buff.toString();
}
} else {
int maxrows = getMaxrows();
stat.setMaxRows(maxrows);
session.executingStatement = stat;
boolean isResultSet;
if (generatedKeys == null) {
isResultSet = stat.execute(sql);
} else if (generatedKeys instanceof Boolean) {
isResultSet = stat.execute(sql,
((Boolean) generatedKeys) ? Statement.RETURN_GENERATED_KEYS : Statement.NO_GENERATED_KEYS);
} else if (generatedKeys instanceof String[]) {
isResultSet = stat.execute(sql, (String[]) generatedKeys);
} else {
isResultSet = stat.execute(sql, (int[]) generatedKeys);
}
session.addCommand(sql);
if (generatedKeys != null) {
rs = null;
rs = stat.getGeneratedKeys();
} else {
if (!isResultSet) {
long updateCount;
try {
updateCount = stat.getLargeUpdateCount();
} catch (UnsupportedOperationException e) {
updateCount = stat.getUpdateCount();
}
buff.append("${text.result.updateCount}: ").append(updateCount);
time = System.currentTimeMillis() - time;
buff.append(" (").append(time).append(" ms)");
stat.close();
return buff.toString();
}
rs = stat.getResultSet();
}
}
time = System.currentTimeMillis() - time;
buff.append(getResultSet(sql, rs, metadata, list, edit, time, allowEdit));
// SQLWarning warning = stat.getWarnings();
// if (warning != null) {
// buff.append(" Warning: ").
// append(getStackTrace(id, warning));
// }
if (!edit) {
stat.close();
}
return buff.toString();
} catch (Throwable e) {
// throwable: including OutOfMemoryError and so on
return getStackTrace(id, e, session.getContents().isH2());
} finally {
session.executingStatement = null;
}
}
private String executeLoop(Connection conn, int count, String sql)
throws SQLException {
ArrayList params = new ArrayList<>();
int idx = 0;
while (!stop) {
idx = sql.indexOf('?', idx);
if (idx < 0) {
break;
}
if (JdbcUtils.isBuiltIn(sql.substring(idx), "?/*rnd*/")) {
params.add(1);
sql = sql.substring(0, idx) + "?" + sql.substring(idx + "/*rnd*/".length() + 1);
} else {
params.add(0);
}
idx++;
}
boolean prepared;
Random random = new Random(1);
long time = System.currentTimeMillis();
if (JdbcUtils.isBuiltIn(sql, "@statement")) {
sql = StringUtils.trimSubstring(sql, "@statement".length());
prepared = false;
Statement stat = conn.createStatement();
for (int i = 0; !stop && i < count; i++) {
String s = sql;
for (Integer type : params) {
idx = s.indexOf('?');
if (type == 1) {
s = s.substring(0, idx) + random.nextInt(count) + s.substring(idx + 1);
} else {
s = s.substring(0, idx) + i + s.substring(idx + 1);
}
}
if (stat.execute(s)) {
ResultSet rs = stat.getResultSet();
while (!stop && rs.next()) {
// maybe get the data as well
}
rs.close();
}
}
} else {
prepared = true;
PreparedStatement prep = conn.prepareStatement(sql);
for (int i = 0; !stop && i < count; i++) {
for (int j = 0; j < params.size(); j++) {
Integer type = params.get(j);
if (type == 1) {
prep.setInt(j + 1, random.nextInt(count));
} else {
prep.setInt(j + 1, i);
}
}
if (session.getContents().isSQLite()) {
// SQLite currently throws an exception on prep.execute()
prep.executeUpdate();
} else {
if (prep.execute()) {
ResultSet rs = prep.getResultSet();
while (!stop && rs.next()) {
// maybe get the data as well
}
rs.close();
}
}
}
}
time = System.currentTimeMillis() - time;
StringBuilder builder = new StringBuilder().append(time).append(" ms: ").append(count).append(" * ")
.append(prepared ? "(Prepared) " : "(Statement) ").append('(');
for (int i = 0, size = params.size(); i < size; i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(params.get(i) == 0 ? "i" : "rnd");
}
return builder.append(") ").append(sql).toString();
}
private String getCommandHistoryString() {
StringBuilder buff = new StringBuilder();
ArrayList history = session.getCommandHistory();
buff.append("" +
"Command ");
for (int i = history.size() - 1; i >= 0; i--) {
String sql = history.get(i);
buff.append("").
append(" ").
append("").
append(PageParser.escapeHtml(sql)).
append(" ");
}
buff.append("
");
return buff.toString();
}
private static String getParameterResultSet(ParameterMetaData meta)
throws SQLException {
StringBuilder buff = new StringBuilder();
if (meta == null) {
return "No parameter meta data";
}
buff.append("").
append("className mode type ").
append("typeName precision scale ");
for (int i = 0; i < meta.getParameterCount(); i++) {
buff.append("").
append(meta.getParameterClassName(i + 1)).
append(" ").
append(meta.getParameterMode(i + 1)).
append(" ").
append(meta.getParameterType(i + 1)).
append(" ").
append(meta.getParameterTypeName(i + 1)).
append(" ").
append(meta.getPrecision(i + 1)).
append(" ").
append(meta.getScale(i + 1)).
append(" ");
}
buff.append("
");
return buff.toString();
}
private String getResultSet(String sql, ResultSet rs, boolean metadata,
boolean list, boolean edit, long time, boolean allowEdit)
throws SQLException {
int maxrows = getMaxrows();
time = System.currentTimeMillis() - time;
StringBuilder buff = new StringBuilder();
if (edit) {
buff.append("