/******************************************************************************
* Copyright © 2013-2016 The Nxt Core Developers. *
* *
* See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* Nxt software, including this file, may be copied, modified, propagated, *
* or distributed except according to the terms contained in the LICENSE.txt *
* file. *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
package nxt.http;
import nxt.Db;
import nxt.util.Convert;
import org.h2.tools.Shell;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URLEncoder;
import java.sql.SQLException;
public final class DbShellServlet extends HttpServlet {
private static final String header =
"<!DOCTYPE html>\n" +
"<html>\n" +
"<head>\n" +
" <meta charset=\"UTF-8\"/>\n" +
" <title>Nxt H2 Database Shell</title>\n" +
" <script type=\"text/javascript\">\n" +
" function submitForm(form, adminPassword) {\n" +
" var url = '/dbshell';\n" +
" var params = '';\n" +
" for (i = 0; i < form.elements.length; i++) {\n" +
" if (! form.elements[i].name) {\n" +
" continue;\n" +
" }\n" +
" if (i > 0) {\n" +
" params += '&';\n" +
" }\n" +
" params += encodeURIComponent(form.elements[i].name);\n" +
" params += '=';\n" +
" params += encodeURIComponent(form.elements[i].value);\n" +
" }\n" +
" if (adminPassword && form.elements.length > 0) {\n" +
" params += '&adminPassword=' + adminPassword;\n" +
" }\n" +
" var request = new XMLHttpRequest();\n" +
" request.open(\"POST\", url, false);\n" +
" request.setRequestHeader(\"Content-type\", \"application/x-www-form-urlencoded\");\n" +
" request.send(params);\n" +
" form.getElementsByClassName(\"result\")[0].textContent += request.responseText;\n" +
" return false;\n" +
" }\n" +
" </script>\n" +
"</head>\n" +
"<body>\n";
private static final String footer =
"</body>\n" +
"</html>\n";
private static final String form =
"<form action=\"/dbshell\" method=\"POST\" onsubmit=\"return submitForm(this" +
(API.disableAdminPassword ? "" : ", '{adminPassword}'") + ");\">" +
"<table class=\"table\" style=\"width:90%;\">" +
"<tr><td><pre class=\"result\" style=\"float:top;width:90%;\">" +
"This is a database shell. Enter SQL to be evaluated, or \"help\" for help:" +
"</pre></td></tr>" +
"<tr><td><b>></b> <input type=\"text\" name=\"line\" style=\"width:90%;\"/></td></tr>" +
"</table>" +
"</form>";
private static final String errorNoPasswordIsConfigured =
"This page is password-protected, but no password is configured in nxt.properties. " +
"Please set nxt.adminPassword or disable the password protection with nxt.disableAdminPassword";
private static final String passwordFormTemplate =
"<form action=\"/dbshell\" method=\"POST\">" +
"<table class=\"table\">" +
"<tr><td colspan=\"3\">%s</td></tr>" +
"<tr>" +
"<td>Password:</td>" +
"<td><input type=\"password\" name=\"adminPassword\"/>" +
"<input type=\"submit\" value=\"Go!\"/></td>" +
"</tr>" +
"</table>" +
"<input type=\"hidden\" name=\"showShell\" value=\"true\"/>" +
"</form>";
private static final String passwordForm = String.format(passwordFormTemplate,
"<p>This page is password-protected. Please enter the administrator's password</p>");
private static final String passwordFormWrongPassword = String.format(passwordFormTemplate,
"<p style=\"color:red\">The provided password does not match the value of nxt.adminPassword. Please try again!</p>");
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate, private");
resp.setHeader("Pragma", "no-cache");
resp.setDateHeader("Expires", 0);
if (! API.isAllowed(req.getRemoteHost())) {
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
String body;
if (API.disableAdminPassword) {
body = form;
} else {
if (API.adminPassword.isEmpty()) {
body = errorNoPasswordIsConfigured;
} else {
body = passwordForm;
}
}
try (PrintStream out = new PrintStream(resp.getOutputStream())) {
out.print(header);
out.print(body);
out.print(footer);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate, private");
resp.setHeader("Pragma", "no-cache");
resp.setDateHeader("Expires", 0);
if (! API.isAllowed(req.getRemoteHost())) {
resp.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}
String body = null;
if (!API.disableAdminPassword) {
if (API.adminPassword.isEmpty()) {
body = errorNoPasswordIsConfigured;
} else {
String adminPassword = req.getParameter("adminPassword");
if (API.adminPassword.equals(adminPassword)) {
if ("true".equals(req.getParameter("showShell"))) {
body = form.replace("{adminPassword}", URLEncoder.encode(adminPassword, "UTF-8") );
}
} else {
body = passwordFormWrongPassword;
}
}
}
if (body != null) {
try (PrintStream out = new PrintStream(resp.getOutputStream())) {
out.print(header);
out.print(body);
out.print(footer);
}
return;
}
String line = Convert.nullToEmpty(req.getParameter("line"));
try (PrintStream out = new PrintStream(resp.getOutputStream())) {
out.println("\n> " + line);
try {
Shell shell = new Shell();
shell.setErr(out);
shell.setOut(out);
shell.runTool(Db.db.getConnection(), "-sql", line);
} catch (SQLException e) {
out.println(e.toString());
}
}
}
}