Compare commits
2 commits
ba0a8f20de
...
f5639ddf44
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5639ddf44 | ||
|
|
ccbc9dc4be |
38 changed files with 3503 additions and 0 deletions
21
.classpath
Normal file
21
.classpath
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry including="**/*.java" kind="src" output="target/classes" path="src">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
target/
|
||||
sql/
|
||||
hs_err*.log
|
||||
37
.project
Normal file
37
.project
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.stephenschafer.email</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.common.project.facet.core.builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
||||
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
12
.settings/.jsdtscope
Normal file
12
.settings/.jsdtscope
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry excluding="**/bower_components/*|**/node_modules/*|**/*.min.js" kind="src" path="WebContent"/>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
|
||||
<attributes>
|
||||
<attribute name="hide" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
|
||||
<classpathentry kind="output" path=""/>
|
||||
</classpath>
|
||||
11
.settings/org.eclipse.jdt.core.prefs
Normal file
11
.settings/org.eclipse.jdt.core.prefs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
|
||||
org.eclipse.jdt.core.compiler.release=disabled
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
4
.settings/org.eclipse.m2e.core.prefs
Normal file
4
.settings/org.eclipse.m2e.core.prefs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
||||
17
.settings/org.eclipse.wst.common.component
Normal file
17
.settings/org.eclipse.wst.common.component
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
|
||||
|
||||
<wb-module deploy-name="com.stephenschafer.email-manager-0.0.1-SNAPSHOT">
|
||||
|
||||
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
||||
|
||||
<wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
|
||||
|
||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
|
||||
|
||||
<property name="context-root" value="com.stephenschafer.email-manager"/>
|
||||
|
||||
<property name="java-output-path" value="/com.stephenschafer.email-manager/build/classes"/>
|
||||
|
||||
</wb-module>
|
||||
|
||||
</project-modules>
|
||||
9
.settings/org.eclipse.wst.common.project.facet.core.xml
Normal file
9
.settings/org.eclipse.wst.common.project.facet.core.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<faceted-project>
|
||||
<fixed facet="jst.web"/>
|
||||
<fixed facet="java"/>
|
||||
<fixed facet="wst.jsdt.web"/>
|
||||
<installed facet="java" version="1.8"/>
|
||||
<installed facet="wst.jsdt.web" version="1.0"/>
|
||||
<installed facet="jst.web" version="2.5"/>
|
||||
</faceted-project>
|
||||
1
.settings/org.eclipse.wst.jsdt.ui.superType.container
Normal file
1
.settings/org.eclipse.wst.jsdt.ui.superType.container
Normal file
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.wst.jsdt.launching.baseBrowserLibrary
|
||||
1
.settings/org.eclipse.wst.jsdt.ui.superType.name
Normal file
1
.settings/org.eclipse.wst.jsdt.ui.superType.name
Normal file
|
|
@ -0,0 +1 @@
|
|||
Window
|
||||
2
.settings/org.eclipse.wst.validation.prefs
Normal file
2
.settings/org.eclipse.wst.validation.prefs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
disabled=06target
|
||||
eclipse.preferences.version=1
|
||||
3
WebContent/META-INF/MANIFEST.MF
Normal file
3
WebContent/META-INF/MANIFEST.MF
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Manifest-Version: 1.0
|
||||
Class-Path:
|
||||
|
||||
52
WebContent/WEB-INF/web.xml
Normal file
52
WebContent/WEB-INF/web.xml
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" version="2.4"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd http://xmlns.jcp.org/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
|
||||
xmlns:web="http://xmlns.jcp.org/xml/ns/javaee">
|
||||
|
||||
<session-config>
|
||||
<session-timeout>1440</session-timeout>
|
||||
</session-config>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>PostMapping</servlet-name>
|
||||
<servlet-class>com.stephenschafer.email.PostMapping</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>PostMapping</servlet-name>
|
||||
<url-pattern>/post-mapping</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>NewAlias</servlet-name>
|
||||
<servlet-class>com.stephenschafer.email.NewAlias</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>NewAlias</servlet-name>
|
||||
<url-pattern>/new-alias</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>reset</servlet-name>
|
||||
<servlet-class>com.stephenschafer.email.ResetSession</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>reset</servlet-name>
|
||||
<url-pattern>/reset</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>logout</servlet-name>
|
||||
<servlet-class>com.stephenschafer.email.Logout</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>logout</servlet-name>
|
||||
<url-pattern>/logout</url-pattern>
|
||||
</servlet-mapping>
|
||||
</web-app>
|
||||
202
WebContent/edit.jsp
Normal file
202
WebContent/edit.jsp
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<%@ page import="java.util.Map"%>
|
||||
<%@ page import="java.util.HashMap"%>
|
||||
<%@ page import="java.util.List"%>
|
||||
<%@ page import="java.util.ArrayList"%>
|
||||
<%@ page import="java.util.Set"%>
|
||||
<%@ page import="java.util.HashSet"%>
|
||||
<%@ page import="java.util.Collections"%>
|
||||
<%@ page import="java.util.Comparator"%>
|
||||
<%@ page import="com.stephenschafer.email.Mapping"%>
|
||||
<%@ page import="com.stephenschafer.email.Util"%>
|
||||
<%@ page import="com.stephenschafer.email.User"%>
|
||||
<%
|
||||
final String address = request.getParameter("address");
|
||||
final boolean mobile = "true".equalsIgnoreCase(request.getParameter("mobile"));
|
||||
final User user = Util.identify(session);
|
||||
if (user == null) {
|
||||
session.setAttribute("return-servlet-path", request.getServletPath());
|
||||
session.setAttribute("return-query-string", request.getQueryString());
|
||||
response.sendRedirect("login.jsp");
|
||||
return;
|
||||
}
|
||||
Util.updateSession(session);
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<String> targetList = (List<String>) session.getAttribute("target-list");
|
||||
final Mapping mapping = address == null ? null : Util.getMapping(address);
|
||||
final String username = user.getName();
|
||||
final String selectedTarget = "sandy".equals(username) ? "sandy" : "elephant".equals(username) ? "steve" : "";
|
||||
final String header = mobile ? selectedTarget : "Mapping";
|
||||
final String targetDisplay = mobile ? "display: none" : "";
|
||||
final String disabledAttr = mapping == null ? "" : mapping.isDisabled() ? " checked" : "";
|
||||
final String disableDisplay = mobile ? "display: none" : "";
|
||||
final String descriptionValue = mapping == null ? "" : mapping.getDisplayDescription();
|
||||
final String dateValue = mapping == null ? "" : mapping.getFormattedDate();
|
||||
%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Edit Mapping</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #333;
|
||||
}
|
||||
table {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
table.mapping {
|
||||
}
|
||||
table.mapping tr.mapping {
|
||||
}
|
||||
table.mapping tr.mapping td:first-of-type {
|
||||
padding-right: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@media only screen and (min-resolution:2x) {
|
||||
body {
|
||||
font-size: 300%;
|
||||
}
|
||||
h1 {
|
||||
display: none;
|
||||
}
|
||||
button {
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
input {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
select {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
|
||||
function targetChanged() {
|
||||
var targetSelect = document.mapping.target;
|
||||
var emailInput = document.mapping["email-address"];
|
||||
var request = new XMLHttpRequest();
|
||||
request.open("POST", "new-alias", true);
|
||||
request.onreadystatechange = function() {
|
||||
if(request.readyState != 4) {
|
||||
return;
|
||||
}
|
||||
if(request.status != 200) {
|
||||
alert("Http error " + request.status + " on " + request.statusText);
|
||||
debugger;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var response = JSON.parse(request.responseText);
|
||||
if(response.error != null) {
|
||||
}
|
||||
else {
|
||||
emailInput.value = response.value;
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
debugger;
|
||||
}
|
||||
};
|
||||
request.setRequestHeader("Content-Type", "application/json");
|
||||
request.setRequestHeader("Accept", "application/json");
|
||||
request.send(targetSelect.value);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="targetChanged()">
|
||||
<h1><%=header %></h1>
|
||||
<form action="post-mapping" method="post" name="mapping">
|
||||
<input type="hidden" name="new" value="<%=mapping == null ? "true" : "false" %>"/>
|
||||
<input type="hidden" name="mobile" value="<%=mobile ? "true" : "false" %>"/>
|
||||
<table class="mapping">
|
||||
|
||||
<tr style="<%=targetDisplay%>" >
|
||||
<td>Target</td>
|
||||
<td>
|
||||
<select name="target" onchange="targetChanged()">
|
||||
<%
|
||||
for(final String target : targetList) {
|
||||
final boolean selected = mapping != null ? target.equals(mapping.getTarget()) : selectedTarget.equals(target);
|
||||
final String selectedAttr = selected ? " selected" : "";
|
||||
%> <option value="<%=target%>"<%=selectedAttr%>><%=target%></option>
|
||||
<%
|
||||
}
|
||||
%> </select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Email Address</td>
|
||||
<td>
|
||||
<%
|
||||
if(mapping == null) {
|
||||
%> <input type="text" name="email-address" value="" size="24"/>
|
||||
<%
|
||||
}
|
||||
else {
|
||||
%> <input type="hidden" name="email-address" value="<%=mapping.getAddress()%>" size="24"/>
|
||||
<%=mapping.getAddress()%>
|
||||
<%
|
||||
}
|
||||
%></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>
|
||||
<input type="text" name="description" value="<%=descriptionValue %>" size="24"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<%
|
||||
if(mapping != null) {
|
||||
%><tr>
|
||||
<td>Date</td>
|
||||
<td>
|
||||
<input type="text" name="date" value="<%=dateValue %>" size="10"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<%
|
||||
}
|
||||
%><tr style="<%=disableDisplay %>">
|
||||
<td>Disabled</td>
|
||||
<td>
|
||||
<input type="checkbox" name="disabled" value="true"<%=disabledAttr %>/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<button type="submit">Submit</button>
|
||||
<%
|
||||
if(!mobile) {
|
||||
%> <button type="button" onclick="window.location.href='index.jsp'">List</button>
|
||||
<button type="button" onclick="window.history.back()">Back</button>
|
||||
<%
|
||||
}
|
||||
%></div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
361
WebContent/index.jsp
Normal file
361
WebContent/index.jsp
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ page import="java.util.Map"%>
|
||||
<%@ page import="java.util.HashMap"%>
|
||||
<%@ page import="java.util.List"%>
|
||||
<%@ page import="java.util.ArrayList"%>
|
||||
<%@ page import="java.util.Set"%>
|
||||
<%@ page import="java.util.HashSet"%>
|
||||
<%@ page import="java.util.Collections"%>
|
||||
<%@ page import="java.util.Comparator"%>
|
||||
<%@ page import="com.stephenschafer.email.Logger"%>
|
||||
<%@ page import="com.stephenschafer.email.Mapping"%>
|
||||
<%@ page import="com.stephenschafer.email.Util"%>
|
||||
<%@ page import="com.stephenschafer.email.User"%>
|
||||
<%
|
||||
final User user = Util.identify(session);
|
||||
if (user == null) {
|
||||
session.setAttribute("return-servlet-path", request.getServletPath());
|
||||
session.setAttribute("return-query-string", request.getQueryString());
|
||||
response.sendRedirect("login.jsp");
|
||||
return;
|
||||
}
|
||||
String domainString = request.getParameter("domain");
|
||||
if(domainString == null) {
|
||||
domainString = (String)session.getAttribute("domain-filter");
|
||||
}
|
||||
else {
|
||||
session.setAttribute("domain-filter", domainString);
|
||||
}
|
||||
String targetString = request.getParameter("target");
|
||||
if(targetString == null) {
|
||||
targetString = (String)session.getAttribute("target-filter");
|
||||
}
|
||||
else {
|
||||
session.setAttribute("target-filter", targetString);
|
||||
}
|
||||
String hideDisabledString = request.getParameter("hide-disabled");
|
||||
/*
|
||||
if(hideDisabledString == null) {
|
||||
hideDisabledString = (String) session.getAttribute("hide-disabled-filter");
|
||||
}
|
||||
else {
|
||||
session.setAttribute("hide-disabled-filter", hideDisabledString);
|
||||
}
|
||||
*/
|
||||
final boolean hideDisabled = "hide".equals(hideDisabledString);
|
||||
Util.updateSession(session);
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<String> targetList = (List<String>) session.getAttribute("target-list");
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<String> domainList = (List<String>) session.getAttribute("domain-list");
|
||||
final List<Mapping> mappings = Util.getMappings();
|
||||
Logger.log(String.format("got %d mappings", Integer.valueOf(mappings.size())));
|
||||
final List<String> domainOptions = new ArrayList<>();
|
||||
boolean domainFound = false;
|
||||
for(final String domain : domainList) {
|
||||
final String selected;
|
||||
if(domain.equals(domainString)) {
|
||||
domainFound = true;
|
||||
selected = " selected";
|
||||
}
|
||||
else {
|
||||
selected = "";
|
||||
}
|
||||
domainOptions.add(String.format("<option value=\"%s\"%s>%s</option>", domain, selected, domain));
|
||||
}
|
||||
if(!domainFound) {
|
||||
domainString = null;
|
||||
session.removeAttribute("domain-filter");
|
||||
}
|
||||
final List<String> targetOptions = new ArrayList<>();
|
||||
boolean targetFound = false;
|
||||
for(final String target : targetList) {
|
||||
final String selected;
|
||||
if(target.equals(targetString)) {
|
||||
targetFound = true;
|
||||
selected = " selected";
|
||||
}
|
||||
else {
|
||||
selected = "";
|
||||
}
|
||||
targetOptions.add(String.format("<option value=\"%s\"%s>%s</option>", target, selected, target));
|
||||
}
|
||||
if(!targetFound) {
|
||||
targetString = null;
|
||||
session.removeAttribute("target-filter");
|
||||
}
|
||||
final Map<String, Set<String>> names = new HashMap<>();
|
||||
%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name=""viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Virtual Addresses</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #333;
|
||||
}
|
||||
button {
|
||||
font-size: inherit;
|
||||
}
|
||||
input {
|
||||
font-size: inherit;
|
||||
}
|
||||
select {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
table {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
table.mappings {
|
||||
}
|
||||
table.mappings tr.mapping {
|
||||
}
|
||||
table.mappings tr.mapping td {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
table.mappings tr.mapping td:first-of-type {
|
||||
padding-left: 0px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
table.mappings tr.mapping td:last-of-type {
|
||||
padding-left: 10px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
table.mappings tr.mapping.disabled {
|
||||
color: #999;
|
||||
}
|
||||
table.mappings tr.divider td {
|
||||
height: 1px;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
table.mappings tr.mapping td.address {
|
||||
white-space: nowrap;
|
||||
}
|
||||
table.mappings tr.mapping td.date {
|
||||
white-space: nowrap;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
table.mappings tr.mapping.disabled a {
|
||||
color: #999;
|
||||
}
|
||||
div.new {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
div.new button {
|
||||
color: red;
|
||||
}
|
||||
@media only screen and (min-resolution:2x) {
|
||||
body {
|
||||
font-size: 300%;
|
||||
}
|
||||
h1 {
|
||||
display: none;
|
||||
}
|
||||
div.new>button {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
div.new>input {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
div.new>select {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
div.new>label {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
table.mappings>tbody>tr.mapping>td {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
|
||||
<%
|
||||
if(domainString != null) {
|
||||
out.println(String.format("var domain = \"%s\";\n", domainString));
|
||||
}
|
||||
else {
|
||||
out.println("var domain = null;\n");
|
||||
}
|
||||
if(targetString != null) {
|
||||
out.println(String.format("var target = \"%s\";\n", targetString));
|
||||
}
|
||||
else {
|
||||
out.println("var target = null;\n");
|
||||
}
|
||||
out.println("var hideDisabled = " + hideDisabled + ";\n");
|
||||
%>
|
||||
function doQuery() {
|
||||
var args = [];
|
||||
if(domain != null) {
|
||||
args.push({name: "domain", value: domain});
|
||||
}
|
||||
if(target != null) {
|
||||
args.push({name: "target", value: target});
|
||||
}
|
||||
if(hideDisabled) {
|
||||
args.push({name: "hide-disabled", value: "hide"});
|
||||
}
|
||||
for(var i = 0; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
args[i] = arg.name + "=" + arg.value;
|
||||
}
|
||||
args = args.join("&");
|
||||
var href = "index.jsp";
|
||||
if(args != "") {
|
||||
href += "?" + args;
|
||||
}
|
||||
window.location.href = href;
|
||||
}
|
||||
|
||||
function selectDomain(select) {
|
||||
domain = select.value;
|
||||
doQuery();
|
||||
}
|
||||
|
||||
function selectTarget(select) {
|
||||
target = select.value;
|
||||
doQuery();
|
||||
}
|
||||
|
||||
function addressFilter(input) {
|
||||
textFilter(input, "address");
|
||||
}
|
||||
|
||||
function descriptionFilter(input) {
|
||||
textFilter(input, "description");
|
||||
}
|
||||
|
||||
function textFilter(input, name) {
|
||||
var regex = new RegExp(input.value == "" ? ".*" : input.value, "i");
|
||||
var tds = document.body.querySelectorAll("table.mappings tr.mapping td." + name);
|
||||
for(var i = 0; i < tds.length; i++) {
|
||||
var td = tds[i];
|
||||
var address = td.textContent;
|
||||
var show = regex.test(address);
|
||||
var tr = td.parentNode;
|
||||
tr.style.display = show ? "" : "none";
|
||||
var tr = tr.nextSibling;
|
||||
while(tr != null) {
|
||||
if(tr.nodeName == "TR") {
|
||||
tr.style.display = show ? "" : "none";
|
||||
break;
|
||||
}
|
||||
tr = tr.nextSibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Virtual Addresses</h1>
|
||||
<div class="new">
|
||||
<button type="button" onclick="window.location.href='edit.jsp'">Add a new mapping</button>
|
||||
<input type="text" name="address-filter" onkeyup="addressFilter(this)" placeholder="address"/>
|
||||
<select onchange="selectDomain(this)">
|
||||
<option value="">All domains</option>
|
||||
<%
|
||||
for(final String option : domainOptions) {
|
||||
%> <%=option %>
|
||||
<%
|
||||
}
|
||||
%> </select>
|
||||
<select onchange="selectTarget(this)">
|
||||
<option value="">All targets</option>
|
||||
<%
|
||||
for(final String option : targetOptions) {
|
||||
%> <%=option %>
|
||||
<%
|
||||
}
|
||||
%> </select>
|
||||
<input type="text" name="description-filter" onkeyup="descriptionFilter(this)" placeholder="description"/>
|
||||
<%
|
||||
final String hideDisabledChecked = hideDisabled ? " checked" : "";
|
||||
%> <label>
|
||||
|
||||
<input type="checkbox" name="hide-disabled" value="hide"<%=hideDisabledChecked %>
|
||||
onclick="hideDisabled = this.checked; doQuery()"/> Hide disabled
|
||||
</label>
|
||||
</div>
|
||||
<table class="mappings">
|
||||
<%
|
||||
for(final Mapping mapping : mappings) {
|
||||
if(!mapping.qualifies(user)) {
|
||||
continue;
|
||||
}
|
||||
if(domainString != null && domainString.trim().length() > 0 && !domainString.equals(mapping.getDomain())) {
|
||||
continue;
|
||||
}
|
||||
if(targetString != null && targetString.trim().length() > 0 && !targetString.equals(mapping.getTarget())) {
|
||||
continue;
|
||||
}
|
||||
if(hideDisabled && mapping.isDisabled()) {
|
||||
continue;
|
||||
}
|
||||
final String disabledClass = mapping.isDisabled() ? " disabled" : "";
|
||||
%><tr class="mapping<%=disabledClass%>">
|
||||
<td class="address"><a href="edit.jsp?address=<%=mapping.getAddress()%>"><%=mapping.getAddress()%></a></td>
|
||||
<td class="target"><%=mapping.getTarget()%></td>
|
||||
<td class="description"><%=mapping.getDisplayDescription()%></td>
|
||||
<td class="disabled"><%=mapping.isDisabled() ? "disabled" : "" %></td>
|
||||
<td class="date"><%=mapping.getFormattedDate() %></td>
|
||||
</tr>
|
||||
<tr class="divider">
|
||||
<td colspan="5"></td>
|
||||
</tr>
|
||||
|
||||
<%
|
||||
if(!mapping.isDisabled()) {
|
||||
String mappingDomain = mapping.getDomain();
|
||||
if(mappingDomain == null) {
|
||||
mappingDomain = "";
|
||||
}
|
||||
Set<String> namesForDomain = names.get(mappingDomain);
|
||||
if(namesForDomain == null) {
|
||||
namesForDomain = new HashSet<>();
|
||||
names.put(mappingDomain, namesForDomain);
|
||||
}
|
||||
namesForDomain.add(mapping.getName());
|
||||
}
|
||||
}
|
||||
|
||||
%></table>
|
||||
<%
|
||||
final List<String> domains = new ArrayList<>(names.keySet());
|
||||
Collections.sort(domains);
|
||||
for(String mappingDomain : domains) {
|
||||
List<String> namesForDomain = new ArrayList<>(names.get(mappingDomain));
|
||||
Collections.sort(namesForDomain);
|
||||
%><div class="names">
|
||||
<h2>Names for <%=mappingDomain %></h2>
|
||||
<%
|
||||
String sep = "";
|
||||
for(final String name : namesForDomain) {
|
||||
out.print(sep);
|
||||
sep = ", ";
|
||||
out.println(name);
|
||||
}
|
||||
%></div>
|
||||
<%
|
||||
}
|
||||
%></body>
|
||||
</html>
|
||||
121
WebContent/login.jsp
Normal file
121
WebContent/login.jsp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<%@ page import="com.stephenschafer.email.Util"%>
|
||||
<%@ page import="com.stephenschafer.email.Configuration"%>
|
||||
<%@ page import="com.stephenschafer.email.Session"%>
|
||||
<%@ page import="com.stephenschafer.email.User"%>
|
||||
<%@ page import="com.stephenschafer.email.Logger"%>
|
||||
<%
|
||||
final boolean failed;
|
||||
final String username = request.getParameter("username");
|
||||
User user = null;
|
||||
if ("post".equalsIgnoreCase(request.getMethod())) {
|
||||
final String password = request.getParameter("password");
|
||||
user = Util.login(username, password);
|
||||
failed = true;
|
||||
}
|
||||
else {
|
||||
final String privilegedHost = Configuration.INSTANCE.getPrivilegedHost();
|
||||
if (username != null && privilegedHost != null ) {
|
||||
String remoteHost = request.getRemoteHost();
|
||||
if (privilegedHost.equals(remoteHost)) {
|
||||
user = Util.login(username);
|
||||
}
|
||||
}
|
||||
failed = false;
|
||||
session.removeAttribute("email-session");
|
||||
}
|
||||
if (user != null) {
|
||||
Session timesheetSession = new Session();
|
||||
timesheetSession.setUser(user);
|
||||
session.setAttribute("email-session", timesheetSession);
|
||||
String returnServletPath = (String) session.getAttribute("return-servlet-path");
|
||||
if (returnServletPath == null) {
|
||||
returnServletPath = "/index.jsp";
|
||||
}
|
||||
String returnQueryString = (String) session.getAttribute("return-query-string");
|
||||
if (returnQueryString == null) {
|
||||
returnQueryString = "";
|
||||
}
|
||||
final String returnUrl = request.getContextPath() + returnServletPath + "?"
|
||||
+ returnQueryString;
|
||||
response.sendRedirect(returnUrl);
|
||||
return;
|
||||
}
|
||||
%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Login</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #333;
|
||||
}
|
||||
table {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
table {
|
||||
border-spacing: 0px;
|
||||
}
|
||||
table.td {
|
||||
padding: 0px;
|
||||
}
|
||||
div.buttons {
|
||||
margin-top: 10px;
|
||||
}
|
||||
@media only screen and (min-resolution:2x) {
|
||||
body {
|
||||
font-size: 300%;
|
||||
}
|
||||
h1 {
|
||||
display: none;
|
||||
}
|
||||
button {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
input {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
select {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<%
|
||||
if (failed) {
|
||||
%><div class="fail">That didn't work.</div>
|
||||
<%
|
||||
}
|
||||
%><form name="login" action="login.jsp" method="post">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Username</td>
|
||||
<td><input type="text" name="username" value="<%=username == null ? "" : username%>" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Password</td>
|
||||
<td><input type="password" name="password" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<button type="submit">Login</button>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
133
WebContent/mapping.jsp
Normal file
133
WebContent/mapping.jsp
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<%@ page import="java.util.Map"%>
|
||||
<%@ page import="java.util.HashMap"%>
|
||||
<%@ page import="java.util.List"%>
|
||||
<%@ page import="java.util.ArrayList"%>
|
||||
<%@ page import="java.util.Set"%>
|
||||
<%@ page import="java.util.HashSet"%>
|
||||
<%@ page import="java.util.Collections"%>
|
||||
<%@ page import="java.util.Comparator"%>
|
||||
<%@ page import="com.stephenschafer.email.Mapping"%>
|
||||
<%@ page import="com.stephenschafer.email.Util"%>
|
||||
<%@ page import="com.stephenschafer.email.User"%>
|
||||
<%
|
||||
final String address = request.getParameter("address");
|
||||
final User user = Util.identify(session);
|
||||
if (user == null) {
|
||||
session.setAttribute("return-servlet-path", request.getServletPath());
|
||||
session.setAttribute("return-query-string", request.getQueryString());
|
||||
response.sendRedirect("login.jsp");
|
||||
return;
|
||||
}
|
||||
Util.updateSession(session);
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<String> targetList = (List<String>) session.getAttribute("target-list");
|
||||
final Mapping mapping = address == null ? null : Util.getMapping(address);
|
||||
final String username = user.getName();
|
||||
final String header = "Mapping";
|
||||
final String descriptionValue = mapping == null ? "" : mapping.getDisplayDescription();
|
||||
final String dateValue = mapping == null ? "" : mapping.getFormattedDate();
|
||||
%>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Mapping</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #333;
|
||||
}
|
||||
table {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
table.mapping tr td {
|
||||
padding-right: 1em;
|
||||
}
|
||||
table.mapping tr td:first-of-type {
|
||||
white-space: nowrap;
|
||||
}
|
||||
div.buttons {
|
||||
margin-top: 2em;
|
||||
}
|
||||
@media only screen and (min-resolution:2x) {
|
||||
body {
|
||||
font-size: 300%;
|
||||
}
|
||||
h1 {
|
||||
display: none;
|
||||
}
|
||||
button {
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
input {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
input[type="checkbox"] {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
select {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: .5em;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body">
|
||||
<h1><%=header %></h1>
|
||||
<table class="mapping">
|
||||
|
||||
<tr>
|
||||
<td>Target</td>
|
||||
<td>
|
||||
<%=mapping.getTarget()%>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Email Address</td>
|
||||
<td>
|
||||
<%=mapping.getAddress()%>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>
|
||||
<%=mapping.getDisplayDescription()%>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Date</td>
|
||||
<td>
|
||||
<%=mapping.getFormattedDate()%>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Disabled</td>
|
||||
<td>
|
||||
<%=mapping.isDisabled() ? "true" : "false" %>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<button type="button" onclick="window.location.href='edit.jsp?mobile=true'">New</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
87
WebContent/password.jsp
Normal file
87
WebContent/password.jsp
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<%@ page import="com.stephenschafer.email.Util"%>
|
||||
<%@ page import="com.stephenschafer.email.Session"%>
|
||||
<%@ page import="com.stephenschafer.email.User"%>
|
||||
<%
|
||||
final User user = Util.identify(session);
|
||||
if (user == null) {
|
||||
session.setAttribute("return-servlet-path", request.getServletPath());
|
||||
session.setAttribute("return-query-string", request.getQueryString());
|
||||
response.sendRedirect("login.jsp");
|
||||
return;
|
||||
}
|
||||
String message = null;
|
||||
if ("post".equalsIgnoreCase(request.getMethod())) {
|
||||
final String password1 = request.getParameter("password1");
|
||||
final String password2 = request.getParameter("password2");
|
||||
if (!password1.equals(password2)) {
|
||||
message = "Passwords don't match.";
|
||||
}
|
||||
else {
|
||||
Util.changePassword(user.getId(), password1);
|
||||
response.sendRedirect("index.jsp");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
message = null;
|
||||
}
|
||||
%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Password</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
table {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 0px;
|
||||
}
|
||||
|
||||
table.td {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
div.buttons {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<%
|
||||
if (message != null) {
|
||||
%><div class="fail">
|
||||
<%=message%>
|
||||
</div>
|
||||
<%
|
||||
}
|
||||
%><form name="register" action="register.jsp" method="post">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Password</td>
|
||||
<td><input type="password" name="password1" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Confirm password</td>
|
||||
<td><input type="password" name="password2" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Display name</td>
|
||||
<td><input type="text" name="displayName" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<button type="submit">Register</button>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
100
WebContent/register.jsp
Normal file
100
WebContent/register.jsp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
||||
pageEncoding="UTF-8"%>
|
||||
<%@ page import="com.stephenschafer.email.Util"%>
|
||||
<%@ page import="com.stephenschafer.email.Session"%>
|
||||
<%@ page import="com.stephenschafer.email.User"%>
|
||||
<%
|
||||
final String message;
|
||||
if("post".equalsIgnoreCase(request.getMethod())) {
|
||||
final String username = request.getParameter("username");
|
||||
final String password1 = request.getParameter("password1");
|
||||
final String password2 = request.getParameter("password2");
|
||||
final String displayName = request.getParameter("displayName");
|
||||
final boolean canWrite = "true".equals(request.getParameter("canWrite"));
|
||||
if(!password1.equals(password2)) {
|
||||
message = "Passwords don't match.";
|
||||
}
|
||||
else {
|
||||
final User user = Util.register(username, password1, displayName, canWrite);
|
||||
if(user != null) {
|
||||
Session emailSession = new Session();
|
||||
emailSession.setUser(user);
|
||||
session.setAttribute("email-session", emailSession);
|
||||
response.sendRedirect("index.jsp");
|
||||
return;
|
||||
}
|
||||
message = "That didn't work.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
message = null;
|
||||
}
|
||||
|
||||
%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Register</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
table {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 0px;
|
||||
}
|
||||
|
||||
table.td {
|
||||
padding: 0px;
|
||||
}
|
||||
div.buttons {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<%
|
||||
if(message != null) {
|
||||
|
||||
%><div class="fail">
|
||||
<%=message %>
|
||||
</div>
|
||||
<%
|
||||
}
|
||||
|
||||
%><form name="register" action="register.jsp" method="post">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Username</td>
|
||||
<td><input type="text" name="username"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Password</td>
|
||||
<td><input type="password" name="password1"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Confirm password</td>
|
||||
<td><input type="password" name="password2"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Display name</td>
|
||||
<td><input type="text" name="displayName"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Can write</td>
|
||||
<td><input type="checkbox" name="canWrite" value="true"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<button type="submit">Register</button>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
9
build
Executable file
9
build
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
ROOT=$(pwd)
|
||||
mkdir -p logs
|
||||
if ! mvn clean package > logs/build.log 2> logs/build.err.log; then
|
||||
echo "build failed"
|
||||
exit 1
|
||||
fi
|
||||
echo "success"
|
||||
5
deploy
Executable file
5
deploy
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||
ssh pi@raspi "mv ~/tomcat/webapps/email.war ~/tomcat/webapps/email.war_$TIMESTAMP"
|
||||
scp target/com.stephenschafer.email-manager-0.0.2-SNAPSHOT.war pi@raspi:~/tomcat/webapps/email.war
|
||||
0
logs/build.err.log
Normal file
0
logs/build.err.log
Normal file
42
logs/build.log
Normal file
42
logs/build.log
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
[INFO] Scanning for projects...
|
||||
[INFO]
|
||||
[INFO] --< com.stephenschafer.email-manager:com.stephenschafer.email-manager >--
|
||||
[INFO] Building com.stephenschafer.email-manager 0.0.2-SNAPSHOT
|
||||
[INFO] from pom.xml
|
||||
[INFO] --------------------------------[ war ]---------------------------------
|
||||
[INFO]
|
||||
[INFO] --- clean:3.2.0:clean (default-clean) @ com.stephenschafer.email-manager ---
|
||||
[INFO] Deleting /disk1/home/sschafer/projects/com.stephenschafer.email/target
|
||||
[INFO]
|
||||
[INFO] --- resources:3.3.1:resources (default-resources) @ com.stephenschafer.email-manager ---
|
||||
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
|
||||
[INFO] skip non existing resourceDirectory /disk1/home/sschafer/projects/com.stephenschafer.email/src/main/resources
|
||||
[INFO]
|
||||
[INFO] --- compiler:3.13.0:compile (default-compile) @ com.stephenschafer.email-manager ---
|
||||
[INFO] Recompiling the module because of changed source code.
|
||||
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
|
||||
[INFO] Compiling 14 source files with javac [debug target 1.8] to target/classes
|
||||
[WARNING] bootstrap class path not set in conjunction with -source 8
|
||||
[INFO]
|
||||
[INFO] --- resources:3.3.1:testResources (default-testResources) @ com.stephenschafer.email-manager ---
|
||||
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
|
||||
[INFO] skip non existing resourceDirectory /disk1/home/sschafer/projects/com.stephenschafer.email/src/test/resources
|
||||
[INFO]
|
||||
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ com.stephenschafer.email-manager ---
|
||||
[INFO] No sources to compile
|
||||
[INFO]
|
||||
[INFO] --- surefire:3.2.2:test (default-test) @ com.stephenschafer.email-manager ---
|
||||
[INFO] No tests to run.
|
||||
[INFO]
|
||||
[INFO] --- war:3.4.0:war (default-war) @ com.stephenschafer.email-manager ---
|
||||
[INFO] Packaging webapp
|
||||
[INFO] Assembling webapp [com.stephenschafer.email-manager] in [/disk1/home/sschafer/projects/com.stephenschafer.email/target/com.stephenschafer.email-manager-0.0.2-SNAPSHOT]
|
||||
[INFO] Processing war project
|
||||
[INFO] Copying webapp resources [/disk1/home/sschafer/projects/com.stephenschafer.email/WebContent]
|
||||
[INFO] Building war: /disk1/home/sschafer/projects/com.stephenschafer.email/target/com.stephenschafer.email-manager-0.0.2-SNAPSHOT.war
|
||||
[INFO] ------------------------------------------------------------------------
|
||||
[INFO] BUILD SUCCESS
|
||||
[INFO] ------------------------------------------------------------------------
|
||||
[INFO] Total time: 2.078 s
|
||||
[INFO] Finished at: 2024-11-28T08:07:33-07:00
|
||||
[INFO] ------------------------------------------------------------------------
|
||||
51
pom.xml
Normal file
51
pom.xml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.stephenschafer.email-manager</groupId>
|
||||
<artifactId>com.stephenschafer.email-manager</artifactId>
|
||||
<version>0.0.2-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
<build>
|
||||
<sourceDirectory>src</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>3.4.0</version>
|
||||
<configuration>
|
||||
<warSourceDirectory>WebContent</warSourceDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.38</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.18.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.18.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
120
src/com/stephenschafer/email/Configuration.java
Normal file
120
src/com/stephenschafer/email/Configuration.java
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public class Configuration {
|
||||
public static final Configuration INSTANCE;
|
||||
private String jndiName;
|
||||
private DbConnectionPool pool;
|
||||
private String privilegedHost;
|
||||
private String defaultDomain;
|
||||
private boolean loaded;
|
||||
|
||||
private Configuration() {
|
||||
jndiName = null;
|
||||
pool = null;
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
public void load() throws IOException, ClassNotFoundException, SQLException {
|
||||
if (loaded) {
|
||||
return;
|
||||
}
|
||||
final Properties systemProps = System.getProperties();
|
||||
final String catalinaHome = systemProps.getProperty("catalina.home");
|
||||
String propertiesFileName = System.getenv("EMAIL_PROPERTIES");
|
||||
if (propertiesFileName == null) {
|
||||
propertiesFileName = catalinaHome + "/conf/Catalina/localhost/email.properties";
|
||||
}
|
||||
final File propertiesFile = new File(propertiesFileName);
|
||||
final Properties properties = new Properties();
|
||||
try (FileInputStream fis = new FileInputStream(propertiesFile)) {
|
||||
properties.load(fis);
|
||||
}
|
||||
Logger.logFilename = properties.getProperty("log.filename");
|
||||
Logger.log("********************************* Starting");
|
||||
jndiName = properties.getProperty("db.jndi");
|
||||
final String url = properties.getProperty("db.url");
|
||||
final String username = properties.getProperty("db.username");
|
||||
final String password = properties.getProperty("db.password");
|
||||
if (jndiName == null) {
|
||||
pool = new DbConnectionPool("com.mysql.jdbc.Driver", url, username, password);
|
||||
}
|
||||
privilegedHost = properties.getProperty("priv-host");
|
||||
final String defaultDomain = properties.getProperty("default-domain");
|
||||
this.defaultDomain = defaultDomain == null ? "schafer.cc" : defaultDomain;
|
||||
Logger.log("initialized, jndiName = " + jndiName);
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
public Connection getConnection() throws SQLException, NamingException {
|
||||
final String jndiName = getJndiName();
|
||||
if (jndiName != null) {
|
||||
final InitialContext initialContext = new InitialContext();
|
||||
final DataSource datasource = (DataSource) initialContext.lookup(jndiName);
|
||||
SQLException lastException = null;
|
||||
int i = 0;
|
||||
while (i < 4) {
|
||||
try {
|
||||
return datasource.getConnection();
|
||||
}
|
||||
catch (final SQLException e) {
|
||||
Logger.log("Failed to get connection, " + (3 - i) + " retrys left", e);
|
||||
lastException = e;
|
||||
try {
|
||||
Thread.sleep(5000L);
|
||||
}
|
||||
catch (final InterruptedException ex) {
|
||||
}
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (lastException != null) {
|
||||
throw lastException;
|
||||
}
|
||||
}
|
||||
return getPool().getConnection(8, false, true);
|
||||
}
|
||||
|
||||
public String getJndiName() {
|
||||
return jndiName;
|
||||
}
|
||||
|
||||
public void setJndiName(final String jndiName) {
|
||||
this.jndiName = jndiName;
|
||||
}
|
||||
|
||||
public DbConnectionPool getPool() {
|
||||
return pool;
|
||||
}
|
||||
|
||||
public void setPool(final DbConnectionPool pool) {
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
static {
|
||||
INSTANCE = new Configuration();
|
||||
}
|
||||
|
||||
public String getPrivilegedHost() {
|
||||
return privilegedHost;
|
||||
}
|
||||
|
||||
public String getDefaultDomain() {
|
||||
return defaultDomain;
|
||||
}
|
||||
|
||||
public void setDefaultDomain(final String defaultDomain) {
|
||||
this.defaultDomain = defaultDomain;
|
||||
}
|
||||
}
|
||||
693
src/com/stephenschafer/email/DbConnectionPool.java
Normal file
693
src/com/stephenschafer/email/DbConnectionPool.java
Normal file
|
|
@ -0,0 +1,693 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.SocketException;
|
||||
import java.sql.Array;
|
||||
import java.sql.Blob;
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.Clob;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.NClob;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLClientInfoException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLWarning;
|
||||
import java.sql.SQLXML;
|
||||
import java.sql.Savepoint;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Struct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class DbConnectionPool implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger LOGGER;
|
||||
private final String uRL;
|
||||
private final String username;
|
||||
private final String password;
|
||||
private final String driver;
|
||||
private final Queue<PooledConnection> connections;
|
||||
private long openConnectionIndex;
|
||||
private final Map<Long, OpenConnectionInfo> openConnections;
|
||||
private long timeout;
|
||||
|
||||
public DbConnectionPool(final String dbDriver, final String dbName, final String dbUsername,
|
||||
final String dbPassword) throws ClassNotFoundException, SQLException {
|
||||
this.connections = new LinkedList<>();
|
||||
this.openConnectionIndex = 0L;
|
||||
this.openConnections = new HashMap<>();
|
||||
this.timeout = 900000L;
|
||||
this.driver = dbDriver;
|
||||
this.uRL = dbName;
|
||||
this.username = dbUsername;
|
||||
this.password = dbPassword;
|
||||
this.validateConnection();
|
||||
}
|
||||
|
||||
public final void validateConnection() throws ClassNotFoundException, SQLException {
|
||||
DbConnectionPool.LOGGER.log(Level.FINEST, "Testing connection pool");
|
||||
DbConnectionPool.LOGGER.log(Level.FINEST, "Instantiating " + this.driver + "\n");
|
||||
Class.forName(this.driver);
|
||||
DbConnectionPool.LOGGER.log(Level.FINEST, "Connecting");
|
||||
final Connection connection = DriverManager.getConnection(this.uRL, this.username,
|
||||
this.password);
|
||||
try {
|
||||
final DatabaseMetaData dbmd = connection.getMetaData();
|
||||
DbConnectionPool.LOGGER.log(Level.FINEST,
|
||||
"Connection to " + dbmd.getDatabaseProductName() + " "
|
||||
+ dbmd.getDatabaseProductVersion() + " successful.");
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
public final void clear() throws SQLException {
|
||||
synchronized (this) {
|
||||
while (!this.connections.isEmpty()) {
|
||||
final PooledConnection connection = this.connections.remove();
|
||||
connection.reallyClose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final PooledConnection getConnection(final int transactionIsolation,
|
||||
final boolean readOnly, final boolean autoCommit) throws SQLException {
|
||||
while (true) {
|
||||
PooledConnection oldConnection;
|
||||
final long timeout;
|
||||
synchronized (this) {
|
||||
if (this.connections.isEmpty()) {
|
||||
oldConnection = null;
|
||||
}
|
||||
else {
|
||||
oldConnection = this.connections.remove();
|
||||
}
|
||||
timeout = this.timeout;
|
||||
}
|
||||
if (oldConnection == null) {
|
||||
DbConnectionPool.LOGGER.log(Level.FINEST, "Connecting");
|
||||
SQLException exception = null;
|
||||
int retryCount = 0;
|
||||
while (retryCount < 10) {
|
||||
Connection newConnection;
|
||||
try {
|
||||
newConnection = DriverManager.getConnection(this.uRL, this.username,
|
||||
this.password);
|
||||
}
|
||||
catch (final SQLException e) {
|
||||
if (!(e.getCause() instanceof SocketException)) {
|
||||
throw e;
|
||||
}
|
||||
exception = e;
|
||||
DbConnectionPool.LOGGER.log(Level.FINEST, "Retrying", e);
|
||||
try {
|
||||
Thread.sleep(1000L);
|
||||
}
|
||||
catch (final InterruptedException ex) {
|
||||
}
|
||||
++retryCount;
|
||||
newConnection = null;
|
||||
}
|
||||
if (newConnection != null) {
|
||||
newConnection.setAutoCommit(true);
|
||||
newConnection.setTransactionIsolation(transactionIsolation);
|
||||
newConnection.setReadOnly(readOnly);
|
||||
newConnection.setAutoCommit(autoCommit);
|
||||
final long index = this.incrementOpenConnectionCount();
|
||||
return new PooledConnection(newConnection, index);
|
||||
}
|
||||
}
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
if (oldConnection != null) {
|
||||
if (oldConnection.isClosed()) {
|
||||
DbConnectionPool.LOGGER.log(Level.WARNING,
|
||||
"Pooled connection was already closed");
|
||||
}
|
||||
else {
|
||||
Label_0439: {
|
||||
Label_0428: {
|
||||
if (timeout != 0L) {
|
||||
if (System.currentTimeMillis()
|
||||
- oldConnection.getLastAccess() >= timeout) {
|
||||
break Label_0428;
|
||||
}
|
||||
}
|
||||
try {
|
||||
oldConnection.setAutoCommit(true);
|
||||
oldConnection.setTransactionIsolation(transactionIsolation);
|
||||
oldConnection.setReadOnly(readOnly);
|
||||
oldConnection.setAutoCommit(autoCommit);
|
||||
synchronized (this) {
|
||||
final Long key = oldConnection.getIndex();
|
||||
final OpenConnectionInfo info = this.openConnections.get(key);
|
||||
if (info != null) {
|
||||
DbConnectionPool.LOGGER.log(Level.WARNING,
|
||||
"Overwriting open connection info: " + key + " "
|
||||
+ info);
|
||||
}
|
||||
this.openConnections.put(key, new OpenConnectionInfo());
|
||||
}
|
||||
return oldConnection;
|
||||
}
|
||||
catch (final Exception e2) {
|
||||
DbConnectionPool.LOGGER.log(Level.SEVERE,
|
||||
"Unable to reuse DB connection", e2);
|
||||
break Label_0439;
|
||||
}
|
||||
}
|
||||
DbConnectionPool.LOGGER.log(Level.FINEST, "DB connection timed out");
|
||||
try {
|
||||
oldConnection.reallyClose();
|
||||
}
|
||||
catch (final Exception e2) {
|
||||
DbConnectionPool.LOGGER.log(Level.SEVERE,
|
||||
"Unable to really close DB connection", e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long incrementOpenConnectionCount() {
|
||||
synchronized (this) {
|
||||
final long index = this.openConnectionIndex++;
|
||||
final Long key = index;
|
||||
final OpenConnectionInfo info = this.openConnections.get(key);
|
||||
if (info != null) {
|
||||
DbConnectionPool.LOGGER.log(Level.WARNING,
|
||||
"Overwriting open connection info: " + key + " " + info);
|
||||
}
|
||||
this.openConnections.put(key, new OpenConnectionInfo());
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
protected final void add(final PooledConnection connection) {
|
||||
synchronized (this) {
|
||||
final OpenConnectionInfo info = this.openConnections.remove(connection.getIndex());
|
||||
if (info == null) {
|
||||
DbConnectionPool.LOGGER.log(Level.WARNING,
|
||||
"adding orphaned connection: " + connection.getIndex());
|
||||
}
|
||||
else {
|
||||
this.connections.offer(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final long getOpenConnectionIndex() {
|
||||
synchronized (this) {
|
||||
return this.openConnectionIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public final OpenConnectionInfo getOpenConnectionInfo(final long index) {
|
||||
synchronized (this) {
|
||||
return this.openConnections.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
public final int getOpenConnectionCount() {
|
||||
synchronized (this) {
|
||||
return this.openConnections.size();
|
||||
}
|
||||
}
|
||||
|
||||
public final int getPooledConnectionCount() {
|
||||
synchronized (this) {
|
||||
return this.connections.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString() {
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
buf.append("DB ");
|
||||
buf.append(this.uRL);
|
||||
buf.append(" ");
|
||||
synchronized (this) {
|
||||
buf.append("open: ");
|
||||
buf.append(this.openConnections.size());
|
||||
buf.append(", pooled: ");
|
||||
buf.append(this.connections.size());
|
||||
buf.append(", next: ");
|
||||
buf.append(this.openConnectionIndex);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public final long getTimeout() {
|
||||
synchronized (this) {
|
||||
return this.timeout;
|
||||
}
|
||||
}
|
||||
|
||||
public final void setTimeout(final long timeout) {
|
||||
synchronized (this) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
}
|
||||
|
||||
public final String getDriver() {
|
||||
return this.driver;
|
||||
}
|
||||
|
||||
public final String getURL() {
|
||||
return this.uRL;
|
||||
}
|
||||
|
||||
public final String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
public final String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public synchronized Map<Long, Long> getOpenConnections() {
|
||||
final Map<Long, Long> map = new HashMap<>();
|
||||
for (final Long index : this.openConnections.keySet()) {
|
||||
final OpenConnectionInfo info = this.openConnections.get(index);
|
||||
map.put(index, info.timestamp);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static {
|
||||
LOGGER = Logger.getLogger(DbConnectionPool.class.getName());
|
||||
}
|
||||
|
||||
public static final class OpenConnectionInfo {
|
||||
public final long timestamp;
|
||||
private final List<StackTraceElement> stackTrace;
|
||||
|
||||
public OpenConnectionInfo() {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
final StackTraceElement[] steArray = Thread.currentThread().getStackTrace();
|
||||
final List<StackTraceElement> stackTrace = new ArrayList<>(
|
||||
steArray.length);
|
||||
for (final StackTraceElement element : steArray) {
|
||||
stackTrace.add(element);
|
||||
}
|
||||
this.stackTrace = Collections.unmodifiableList(
|
||||
(List<? extends StackTraceElement>) stackTrace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Timestamp: ");
|
||||
sb.append(new Date(this.timestamp));
|
||||
sb.append("\n");
|
||||
for (final StackTraceElement element : this.stackTrace) {
|
||||
sb.append(element);
|
||||
sb.append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public class PooledConnection implements Connection {
|
||||
private final Connection connection;
|
||||
private long lastAccess;
|
||||
private boolean autoCommit;
|
||||
private boolean readOnly;
|
||||
private final long index;
|
||||
|
||||
public PooledConnection(final Connection connection, final long index) {
|
||||
this.lastAccess = System.currentTimeMillis();
|
||||
this.autoCommit = false;
|
||||
this.readOnly = false;
|
||||
this.index = index;
|
||||
this.connection = connection;
|
||||
try {
|
||||
this.autoCommit = connection.getAutoCommit();
|
||||
}
|
||||
catch (final SQLException e) {
|
||||
DbConnectionPool.LOGGER.log(Level.WARNING, "Unable to get auto commit", e);
|
||||
}
|
||||
try {
|
||||
this.readOnly = connection.isReadOnly();
|
||||
}
|
||||
catch (final SQLException e) {
|
||||
DbConnectionPool.LOGGER.log(Level.WARNING, "Unable to get read only", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearPool() throws SQLException {
|
||||
DbConnectionPool.this.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nativeSQL(final String sql) throws SQLException {
|
||||
return this.connection.nativeSQL(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.connection.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Class<?>> getTypeMap() throws SQLException {
|
||||
return this.connection.getTypeMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(final String sql) throws SQLException {
|
||||
final PreparedStatement stmt = this.connection.prepareStatement(sql);
|
||||
if (this.connection.getAutoCommit() != this.autoCommit
|
||||
|| this.connection.isReadOnly() != this.readOnly) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
String sep = "";
|
||||
if (this.connection.getAutoCommit() != this.autoCommit) {
|
||||
sb.append(sep);
|
||||
sep = " and ";
|
||||
sb.append("autoCommit has changed from " + this.autoCommit);
|
||||
}
|
||||
if (this.connection.isReadOnly() != this.readOnly) {
|
||||
sb.append(sep);
|
||||
sep = " and ";
|
||||
sb.append("readOnly has changed from " + this.readOnly);
|
||||
}
|
||||
sb.append(" in ");
|
||||
sb.append(stmt);
|
||||
DbConnectionPool.LOGGER.log(Level.WARNING, sb.toString());
|
||||
}
|
||||
return stmt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTransactionIsolation(final int level) throws SQLException {
|
||||
this.connection.setTransactionIsolation(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCatalog() throws SQLException {
|
||||
return this.connection.getCatalog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTransactionIsolation() throws SQLException {
|
||||
return this.connection.getTransactionIsolation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseSavepoint(final Savepoint savepoint) throws SQLException {
|
||||
this.connection.releaseSavepoint(savepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHoldability() throws SQLException {
|
||||
return this.connection.getHoldability();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(final String sql, final int resultSetType,
|
||||
final int resultSetConcurrency, final int resultSetHoldability)
|
||||
throws SQLException {
|
||||
return this.connection.prepareCall(sql, resultSetType, resultSetConcurrency,
|
||||
resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAutoCommit() throws SQLException {
|
||||
return this.connection.getAutoCommit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement() throws SQLException {
|
||||
return this.connection.createStatement();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(final String sql) throws SQLException {
|
||||
return this.connection.prepareCall(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAutoCommit(final boolean autoCommit) throws SQLException {
|
||||
this.autoCommit = autoCommit;
|
||||
this.connection.setAutoCommit(autoCommit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys)
|
||||
throws SQLException {
|
||||
return this.connection.prepareStatement(sql, autoGeneratedKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(final boolean readOnly) throws SQLException {
|
||||
this.readOnly = readOnly;
|
||||
this.connection.setReadOnly(readOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallableStatement prepareCall(final String sql, final int resultSetType,
|
||||
final int resultSetConcurrency) throws SQLException {
|
||||
return this.connection.prepareCall(sql, resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLWarning getWarnings() throws SQLException {
|
||||
return this.connection.getWarnings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(final String sql, final int resultSetType,
|
||||
final int resultSetConcurrency) throws SQLException {
|
||||
return this.connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
return this.connection.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes)
|
||||
throws SQLException {
|
||||
return this.connection.prepareStatement(sql, columnIndexes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() throws SQLException {
|
||||
return this.connection.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(final String sql, final int resultSetType,
|
||||
final int resultSetConcurrency, final int resultSetHoldability)
|
||||
throws SQLException {
|
||||
return this.connection.prepareStatement(sql, resultSetType, resultSetConcurrency,
|
||||
resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws SQLException {
|
||||
this.connection.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearWarnings() throws SQLException {
|
||||
this.connection.clearWarnings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCatalog(final String catalog) throws SQLException {
|
||||
this.connection.setCatalog(catalog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
DbConnectionPool.this.add(this);
|
||||
this.lastAccess = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void reallyClose() throws SQLException {
|
||||
this.connection.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.connection.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DatabaseMetaData getMetaData() throws SQLException {
|
||||
return this.connection.getMetaData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() throws SQLException {
|
||||
this.connection.rollback();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Savepoint setSavepoint(final String name) throws SQLException {
|
||||
return this.connection.setSavepoint(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() throws SQLException {
|
||||
return this.connection.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(final int resultSetType, final int resultSetConcurrency)
|
||||
throws SQLException {
|
||||
return this.connection.createStatement(resultSetType, resultSetConcurrency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback(final Savepoint savepoint) throws SQLException {
|
||||
this.connection.rollback(savepoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(final String sql, final String[] columnNames)
|
||||
throws SQLException {
|
||||
return this.connection.prepareStatement(sql, columnNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Savepoint setSavepoint() throws SQLException {
|
||||
return this.connection.setSavepoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createStatement(final int resultSetType, final int resultSetConcurrency,
|
||||
final int resultSetHoldability) throws SQLException {
|
||||
return this.connection.createStatement(resultSetType, resultSetConcurrency,
|
||||
resultSetHoldability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTypeMap(final Map<String, Class<?>> map) throws SQLException {
|
||||
this.connection.setTypeMap(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHoldability(final int holdability) throws SQLException {
|
||||
this.connection.setHoldability(holdability);
|
||||
}
|
||||
|
||||
public long getLastAccess() {
|
||||
return this.lastAccess;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array createArrayOf(final String arg0, final Object[] arg1) throws SQLException {
|
||||
return this.connection.createArrayOf(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blob createBlob() throws SQLException {
|
||||
return this.connection.createBlob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clob createClob() throws SQLException {
|
||||
return this.connection.createClob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NClob createNClob() throws SQLException {
|
||||
return this.connection.createNClob();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLXML createSQLXML() throws SQLException {
|
||||
return this.connection.createSQLXML();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Struct createStruct(final String arg0, final Object[] arg1) throws SQLException {
|
||||
return this.connection.createStruct(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties getClientInfo() throws SQLException {
|
||||
return this.connection.getClientInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientInfo(final String arg0) throws SQLException {
|
||||
return this.connection.getClientInfo(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(final int arg0) throws SQLException {
|
||||
return this.connection.isValid(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(final Properties arg0) throws SQLClientInfoException {
|
||||
this.connection.setClientInfo(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClientInfo(final String arg0, final String arg1)
|
||||
throws SQLClientInfoException {
|
||||
this.connection.setClientInfo(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWrapperFor(final Class<?> arg0) throws SQLException {
|
||||
return this.connection.isWrapperFor(arg0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unwrap(final Class<T> arg0) throws SQLException {
|
||||
return this.connection.unwrap(arg0);
|
||||
}
|
||||
|
||||
public long getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSchema(final String schema) throws SQLException {
|
||||
this.connection.setSchema(schema);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSchema() throws SQLException {
|
||||
return this.connection.getSchema();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort(final Executor executor) throws SQLException {
|
||||
this.connection.abort(executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNetworkTimeout(final Executor executor, final int milliseconds)
|
||||
throws SQLException {
|
||||
this.connection.setNetworkTimeout(executor, milliseconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNetworkTimeout() throws SQLException {
|
||||
return this.connection.getNetworkTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
190
src/com/stephenschafer/email/GlobalCache.java
Normal file
190
src/com/stephenschafer/email/GlobalCache.java
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.SQLException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import com.stephenschafer.email.Util.SaveMappingsResult;
|
||||
|
||||
public class GlobalCache {
|
||||
public static final GlobalCache INSTANCE = new GlobalCache();
|
||||
private static final Pattern COMMENT_PATTERN = Pattern.compile("^ *# *(.*)$");
|
||||
private static final Pattern DATE_PATTERN = Pattern.compile(
|
||||
"^ *# *@([0-9]{4}-[0-9]{2}-[0-9]{2})$");
|
||||
private static final Pattern MAPPING_PATTERN = Pattern.compile("^ *([^ ]+) +(.*)$");
|
||||
private static final Pattern DISABLED_PATTERN = Pattern.compile("^ *# *- *([^ ]+) +(.*)$");
|
||||
private boolean initialized = false;
|
||||
|
||||
public synchronized void initialize() throws IOException, SQLException, NamingException {
|
||||
if (!initialized) {
|
||||
if (Util.getMappingsCountFromDb() == 0) {
|
||||
parseVirtualFile();
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized static void parseVirtualFile()
|
||||
throws IOException, SQLException, NamingException {
|
||||
Logger.log("parsing virtual file");
|
||||
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
final File file = new File("/etc/postfix/virtual");
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
|
||||
String line = reader.readLine();
|
||||
String previousComment = null;
|
||||
Date previousDate = null;
|
||||
while (line != null) {
|
||||
final Matcher disabledMatcher = DISABLED_PATTERN.matcher(line);
|
||||
if (disabledMatcher.matches()) {
|
||||
final String address = disabledMatcher.group(1);
|
||||
final String target = disabledMatcher.group(2);
|
||||
final Mapping mapping = new Mapping(address, target, previousComment,
|
||||
previousDate, true);
|
||||
Util.addMappingToDb(mapping);
|
||||
previousComment = null;
|
||||
previousDate = null;
|
||||
}
|
||||
else {
|
||||
final Matcher datedCommentMatcher = DATE_PATTERN.matcher(line);
|
||||
if (datedCommentMatcher.matches()) {
|
||||
try {
|
||||
final String dateString = datedCommentMatcher.group(1);
|
||||
Logger.log(String.format("found date: %s", dateString));
|
||||
previousDate = df.parse(dateString);
|
||||
}
|
||||
catch (final ParseException e) {
|
||||
Logger.log("Failed to parse date from virtual", e);
|
||||
previousDate = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
final Matcher commentMatcher = COMMENT_PATTERN.matcher(line);
|
||||
if (commentMatcher.matches()) {
|
||||
previousComment = commentMatcher.group(1);
|
||||
}
|
||||
else {
|
||||
final Matcher mappingMatcher = MAPPING_PATTERN.matcher(line);
|
||||
if (mappingMatcher.matches()) {
|
||||
final String from = mappingMatcher.group(1);
|
||||
final String to = mappingMatcher.group(2);
|
||||
final Mapping mapping = new Mapping(from, to, previousComment,
|
||||
previousDate, false);
|
||||
Util.addMappingToDb(mapping);
|
||||
previousComment = null;
|
||||
previousDate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
line = reader.readLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized SaveMappingsResult writeVirtualFile()
|
||||
throws IOException, InterruptedException, SQLException, NamingException {
|
||||
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
final File userHome = new File(System.getProperty("user.home"));
|
||||
final File stagingDir = new File(userHome, "staging");
|
||||
stagingDir.mkdirs();
|
||||
final File file = new File(stagingDir, "virtual");
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
file.createNewFile();
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(file))) {
|
||||
final List<Mapping> list = Util.getMappingsFromDb();
|
||||
for (final Mapping mapping : list) {
|
||||
final String description = mapping.getDescription();
|
||||
if (description != null) {
|
||||
final Date date = mapping.getDate();
|
||||
if (date != null) {
|
||||
writer.println(String.format("#@%s", df.format(date)));
|
||||
}
|
||||
writer.println(String.format("# %s", description));
|
||||
}
|
||||
final boolean disabled = mapping.isDisabled();
|
||||
if (disabled) {
|
||||
writer.print("#-");
|
||||
}
|
||||
writer.println(String.format("%s %s", mapping.getAddress(), mapping.getTarget()));
|
||||
}
|
||||
}
|
||||
final Process process = Runtime.getRuntime().exec(
|
||||
new File(userHome, "update-virtual").getAbsolutePath());
|
||||
final String error = Util.streamToString(process.getErrorStream());
|
||||
final String output = Util.streamToString(process.getInputStream());
|
||||
final int exitValue = process.waitFor();
|
||||
return new SaveMappingsResult() {
|
||||
@Override
|
||||
public int getExitValue() {
|
||||
return exitValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public synchronized boolean addressExists(final String address)
|
||||
throws SQLException, NamingException, IOException {
|
||||
initialize();
|
||||
return Util.getMappingFromDb(address) != null;
|
||||
}
|
||||
|
||||
public synchronized void addMapping(final Mapping mapping)
|
||||
throws IOException, SQLException, NamingException {
|
||||
initialize();
|
||||
Util.addMappingToDb(mapping);
|
||||
}
|
||||
|
||||
public synchronized void addHistory(final Mapping mapping)
|
||||
throws IOException, SQLException, NamingException {
|
||||
initialize();
|
||||
Util.addHistoryToDb(mapping);
|
||||
}
|
||||
|
||||
public void updateMapping(final Mapping mapping)
|
||||
throws IOException, SQLException, NamingException {
|
||||
initialize();
|
||||
Util.updateMappingToDb(mapping);
|
||||
}
|
||||
|
||||
public synchronized Mapping getMapping(final String address)
|
||||
throws IOException, SQLException, NamingException {
|
||||
initialize();
|
||||
return Util.getMappingFromDb(address);
|
||||
}
|
||||
|
||||
public synchronized List<Mapping> getMappings()
|
||||
throws IOException, SQLException, NamingException {
|
||||
initialize();
|
||||
return Util.getMappingsFromDb();
|
||||
}
|
||||
|
||||
public synchronized List<String> getAliases(final String target)
|
||||
throws IOException, SQLException, NamingException {
|
||||
initialize();
|
||||
return Util.getAliasesFromDb(target);
|
||||
}
|
||||
}
|
||||
76
src/com/stephenschafer/email/Logger.java
Normal file
76
src/com/stephenschafer/email/Logger.java
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class Logger {
|
||||
public static String logFilename;
|
||||
public static boolean debugLogging;
|
||||
|
||||
public static void debugLog(final Object message) {
|
||||
if (Logger.debugLogging) {
|
||||
log(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void debugLog(final Object message, final Throwable t) {
|
||||
if (Logger.debugLogging) {
|
||||
log(message, t);
|
||||
}
|
||||
}
|
||||
|
||||
public static void log(final Object message) {
|
||||
log(message, null);
|
||||
}
|
||||
|
||||
public static void log(final Object message, final Throwable t) {
|
||||
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ");
|
||||
try {
|
||||
final String filename = Logger.logFilename;
|
||||
if (filename == null) {
|
||||
final PrintStream out = System.out;
|
||||
out.print(df.format(new Date()));
|
||||
out.print(" ");
|
||||
out.println(message);
|
||||
if (t != null) {
|
||||
t.printStackTrace(out);
|
||||
}
|
||||
}
|
||||
else {
|
||||
final File file = new File(filename);
|
||||
final FileWriter fw = new FileWriter(file, true);
|
||||
final PrintWriter pw = new PrintWriter(fw);
|
||||
try {
|
||||
pw.print(df.format(new Date()));
|
||||
pw.print(" ");
|
||||
pw.println(message);
|
||||
if (t != null) {
|
||||
t.printStackTrace(pw);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
pw.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
System.out.println("Logger: Unable to log " + message);
|
||||
if (t != null) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
log("Log failure caused by:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
Logger.logFilename = null;
|
||||
Logger.debugLogging = false;
|
||||
}
|
||||
}
|
||||
18
src/com/stephenschafer/email/Logout.java
Normal file
18
src/com/stephenschafer/email/Logout.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class Logout extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
Util.logout(req.getSession());
|
||||
}
|
||||
}
|
||||
93
src/com/stephenschafer/email/Mapping.java
Normal file
93
src/com/stephenschafer/email/Mapping.java
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
public class Mapping {
|
||||
private static final Pattern ADDRESS_PATTERN = Pattern.compile("^([^@]+)@(.*)$");
|
||||
private final String address;
|
||||
private final String target;
|
||||
private final String description;
|
||||
private final Date date;
|
||||
private final boolean disabled;
|
||||
|
||||
public Mapping(final String address, final String target, final String description,
|
||||
final Date date, final boolean disabled) {
|
||||
this.address = address;
|
||||
this.target = target;
|
||||
this.description = description;
|
||||
this.date = date;
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public String getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getDisplayDescription() {
|
||||
if (description == null) {
|
||||
return "";
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
public boolean isDisabled() {
|
||||
return disabled;
|
||||
}
|
||||
|
||||
public String getDomain() {
|
||||
final Matcher matcher = ADDRESS_PATTERN.matcher(address);
|
||||
if (matcher.matches()) {
|
||||
return matcher.group(2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
final Matcher matcher = ADDRESS_PATTERN.matcher(address);
|
||||
if (matcher.matches()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean qualifies(final User user) throws SQLException, NamingException {
|
||||
final List<Target> validTargets = user.getValidTargets();
|
||||
if (validTargets == null) {
|
||||
return true;
|
||||
}
|
||||
for (final Target validTarget : validTargets) {
|
||||
if (validTarget.getAddress().equals(target)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public String getFormattedDate() {
|
||||
if (date == null) {
|
||||
return "";
|
||||
}
|
||||
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
return df.format(date);
|
||||
}
|
||||
}
|
||||
87
src/com/stephenschafer/email/NewAlias.java
Normal file
87
src/com/stephenschafer/email/NewAlias.java
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class NewAlias extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
try {
|
||||
final InputStream inputStream = request.getInputStream();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final byte[] buffer = new byte[0x1000];
|
||||
int bytesRead = inputStream.read(buffer);
|
||||
while (bytesRead >= 0) {
|
||||
final String string = new String(buffer, 0, bytesRead);
|
||||
sb.append(string);
|
||||
bytesRead = inputStream.read(buffer);
|
||||
}
|
||||
String target = sb.toString();
|
||||
final int indexOfAt = target.indexOf("@");
|
||||
if (indexOfAt >= 0) {
|
||||
target = target.substring(0, indexOfAt);
|
||||
}
|
||||
final String address = getAddress(target);
|
||||
final PrintWriter out = response.getWriter();
|
||||
out.println("{\"value\":\"" + address + "\"}");
|
||||
}
|
||||
catch (final Exception e) {
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAddress(final String target)
|
||||
throws IOException, SQLException, NamingException {
|
||||
final String defaultDomain = Configuration.INSTANCE.getDefaultDomain();
|
||||
final String patternString = target + "([0-9]+)@" + defaultDomain.replace(".", "\\.");
|
||||
Logger.log("pattern = " + patternString);
|
||||
final Pattern pattern = Pattern.compile(patternString);
|
||||
final List<String> aliasList = Util.getAliases(target);
|
||||
Logger.log("aliasList = " + aliasList);
|
||||
final Set<Integer> indexSet = new HashSet<>();
|
||||
for (final String alias : aliasList) {
|
||||
final Matcher matcher = pattern.matcher(alias);
|
||||
if (matcher.matches()) {
|
||||
indexSet.add(Integer.valueOf(matcher.group(1)));
|
||||
}
|
||||
}
|
||||
final List<Integer> indexList = new ArrayList<>(indexSet);
|
||||
Collections.sort(indexList);
|
||||
Logger.log("indexList = " + indexList);
|
||||
Integer prevIndex = null;
|
||||
for (final Integer index : indexList) {
|
||||
if (prevIndex == null && index.intValue() > 1) {
|
||||
prevIndex = Integer.valueOf(0);
|
||||
break;
|
||||
}
|
||||
if (prevIndex != null && index.intValue() - prevIndex.intValue() > 1) {
|
||||
break;
|
||||
}
|
||||
prevIndex = index;
|
||||
}
|
||||
int useIndex = 1;
|
||||
if (prevIndex != null) {
|
||||
useIndex = prevIndex.intValue() + 1;
|
||||
}
|
||||
Logger.log("useIndex = " + useIndex);
|
||||
return target + useIndex + "@" + defaultDomain;
|
||||
}
|
||||
}
|
||||
103
src/com/stephenschafer/email/PostMapping.java
Normal file
103
src/com/stephenschafer/email/PostMapping.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class PostMapping extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void init(final ServletConfig config) throws ServletException {
|
||||
try {
|
||||
Configuration.INSTANCE.load();
|
||||
GlobalCache.INSTANCE.initialize();
|
||||
}
|
||||
catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
super.init(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
try {
|
||||
final boolean newMapping = "true".equals(request.getParameter("new"));
|
||||
final boolean mobile = "true".equals(request.getParameter("mobile"));
|
||||
final String address = request.getParameter("email-address");
|
||||
final String description = request.getParameter("description");
|
||||
final String target = request.getParameter("target");
|
||||
final boolean disabled = "true".equalsIgnoreCase(request.getParameter("disabled"));
|
||||
if (newMapping) {
|
||||
if (Util.addressExists(address)) {
|
||||
response.sendError(404, "Address already exists");
|
||||
return;
|
||||
}
|
||||
if (address == null || address.length() == 0) {
|
||||
response.sendError(400, "Please supply an address");
|
||||
return;
|
||||
}
|
||||
if (address.indexOf('@') < 0) {
|
||||
response.sendError(400, "Not a valid email address");
|
||||
return;
|
||||
}
|
||||
final Date date = new Date();
|
||||
final Mapping mapping = new Mapping(address, target, description, date, disabled);
|
||||
Util.addMapping(mapping);
|
||||
Util.addHistory(mapping);
|
||||
}
|
||||
else {
|
||||
if (!Util.addressExists(address)) {
|
||||
response.sendError(404, "Address does not exist");
|
||||
return;
|
||||
}
|
||||
final String dateString = request.getParameter("date");
|
||||
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date date;
|
||||
try {
|
||||
date = dateString != null && dateString.trim().length() > 0
|
||||
? df.parse(dateString)
|
||||
: null;
|
||||
}
|
||||
catch (final ParseException e1) {
|
||||
date = null;
|
||||
}
|
||||
final Mapping mapping = new Mapping(address, target, description, date, disabled);
|
||||
Util.updateMapping(mapping);
|
||||
Util.addHistory(mapping);
|
||||
}
|
||||
Util.SaveMappingsResult result;
|
||||
try {
|
||||
result = Util.saveMappings();
|
||||
}
|
||||
catch (final InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
result = null;
|
||||
}
|
||||
if (result != null && result.getExitValue() != 0) {
|
||||
final PrintWriter out = response.getWriter();
|
||||
out.println("saveMappings returned " + result.getExitValue());
|
||||
out.println("Error:");
|
||||
out.println(result.getError());
|
||||
out.println("Output:");
|
||||
out.println(result.getOutput());
|
||||
}
|
||||
else {
|
||||
response.sendRedirect(mobile ? "mapping.jsp?address=" + address : "index.jsp");
|
||||
}
|
||||
}
|
||||
catch (final Exception e) {
|
||||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/com/stephenschafer/email/ResetSession.java
Normal file
18
src/com/stephenschafer/email/ResetSession.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class ResetSession extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
Util.resetSession(req.getSession());
|
||||
}
|
||||
}
|
||||
13
src/com/stephenschafer/email/Server.java
Normal file
13
src/com/stephenschafer/email/Server.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
public class Server {
|
||||
private final String serverName;
|
||||
|
||||
public Server(final String serverName) {
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
public String getServerName() {
|
||||
return serverName;
|
||||
}
|
||||
}
|
||||
13
src/com/stephenschafer/email/Session.java
Normal file
13
src/com/stephenschafer/email/Session.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
public class Session {
|
||||
private User user;
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(final User user) {
|
||||
this.user = user;
|
||||
}
|
||||
}
|
||||
55
src/com/stephenschafer/email/Target.java
Normal file
55
src/com/stephenschafer/email/Target.java
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class Target {
|
||||
private final int id;
|
||||
private final String address;
|
||||
private final String description;
|
||||
private final Date created;
|
||||
|
||||
public Target(final int id, final String address, final String description,
|
||||
final Date created) {
|
||||
if (address == null) {
|
||||
throw new IllegalArgumentException("address may not be null");
|
||||
}
|
||||
if (created == null) {
|
||||
throw new IllegalArgumentException("created may not be null");
|
||||
}
|
||||
this.id = id;
|
||||
this.address = address;
|
||||
this.description = description;
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!(obj instanceof Target)) {
|
||||
return false;
|
||||
}
|
||||
final Target that = (Target) obj;
|
||||
return this.id == that.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// TODO Auto-generated method stub
|
||||
return super.toString();
|
||||
}
|
||||
}
|
||||
43
src/com/stephenschafer/email/User.java
Normal file
43
src/com/stephenschafer/email/User.java
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
public class User {
|
||||
private final int id;
|
||||
private final String name;
|
||||
private final String displayName;
|
||||
private final boolean canWrite;
|
||||
|
||||
public User(final int id, final String name, final String displayName, final boolean canWrite) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.displayName = displayName;
|
||||
this.canWrite = canWrite;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public boolean isCanWrite() {
|
||||
return canWrite;
|
||||
}
|
||||
|
||||
public List<Target> getValidTargets() throws SQLException, NamingException {
|
||||
if ("elephant".equals(name)) {
|
||||
return null; // all targets are valid
|
||||
}
|
||||
return Util.getTargetsForUser(this);
|
||||
}
|
||||
}
|
||||
697
src/com/stephenschafer/email/Util.java
Normal file
697
src/com/stephenschafer/email/Util.java
Normal file
|
|
@ -0,0 +1,697 @@
|
|||
package com.stephenschafer.email;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
public class Util {
|
||||
private Util() {
|
||||
}
|
||||
|
||||
public static void resetSession(final HttpSession session) {
|
||||
session.removeAttribute("target-list");
|
||||
session.removeAttribute("domain-list");
|
||||
}
|
||||
|
||||
public static void updateSession(final HttpSession session)
|
||||
throws IOException, SQLException, NamingException {
|
||||
if (session.getAttribute("target-list") != null) {
|
||||
Logger.log("Util.updateSession session is already up to date");
|
||||
return;
|
||||
}
|
||||
final User user = identify(session);
|
||||
final Set<String> targetSet = new HashSet<>();
|
||||
final Set<String> domainSet = new HashSet<>();
|
||||
for (final Mapping mapping : GlobalCache.INSTANCE.getMappings()) {
|
||||
if (mapping.qualifies(user)) {
|
||||
targetSet.add(mapping.getTarget());
|
||||
}
|
||||
if (mapping.qualifies(user)) {
|
||||
final String domain = mapping.getDomain();
|
||||
if (domain != null) {
|
||||
domainSet.add(domain);
|
||||
}
|
||||
}
|
||||
}
|
||||
Logger.log("Util.updateSession targets: " + targetSet.size());
|
||||
Logger.log("Util.updateSession domains: " + domainSet.size());
|
||||
final List<String> targetList = new ArrayList<>(targetSet);
|
||||
Collections.sort(targetList);
|
||||
final List<String> domainList = new ArrayList<>(domainSet);
|
||||
Collections.sort(domainList);
|
||||
session.setAttribute("target-list", targetList);
|
||||
session.setAttribute("domain-list", domainList);
|
||||
}
|
||||
|
||||
public static User register(final String username, final String password,
|
||||
final String displayName, final boolean canWrite)
|
||||
throws NoSuchAlgorithmException, SQLException, NamingException {
|
||||
final SecureRandom random = new SecureRandom();
|
||||
final byte[] salt = new byte[64];
|
||||
random.nextBytes(salt);
|
||||
final byte[] passwordBytes = password.getBytes();
|
||||
final byte[] saltPlusPassword = new byte[salt.length + passwordBytes.length];
|
||||
int i = 0;
|
||||
for (final byte b : salt) {
|
||||
saltPlusPassword[i++] = b;
|
||||
}
|
||||
for (final byte b : passwordBytes) {
|
||||
saltPlusPassword[i++] = b;
|
||||
}
|
||||
final MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(saltPlusPassword);
|
||||
final byte[] digest = md.digest();
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final String sql = "insert into user"
|
||||
+ " (username, password, salt, display_name, can_write)"
|
||||
+ " values (?, ?, ?, ?, ?)";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql,
|
||||
PreparedStatement.RETURN_GENERATED_KEYS);
|
||||
try {
|
||||
statement.setString(1, username);
|
||||
statement.setBytes(2, digest);
|
||||
statement.setBytes(3, salt);
|
||||
statement.setString(4, displayName);
|
||||
statement.setBoolean(5, canWrite);
|
||||
final int rowCount = statement.executeUpdate();
|
||||
if (rowCount == 0) {
|
||||
return null;
|
||||
}
|
||||
final ResultSet resultSet = statement.getGeneratedKeys();
|
||||
try {
|
||||
if (resultSet.next()) {
|
||||
final int id = resultSet.getInt(1);
|
||||
return new User(id, username, displayName, canWrite);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void changePassword(final int id, final String password)
|
||||
throws NoSuchAlgorithmException, SQLException, NamingException {
|
||||
final SecureRandom random = new SecureRandom();
|
||||
final byte[] salt = new byte[64];
|
||||
random.nextBytes(salt);
|
||||
final byte[] passwordBytes = password.getBytes();
|
||||
final byte[] saltPlusPassword = new byte[salt.length + passwordBytes.length];
|
||||
int i = 0;
|
||||
for (final byte b : salt) {
|
||||
saltPlusPassword[i++] = b;
|
||||
}
|
||||
for (final byte b : passwordBytes) {
|
||||
saltPlusPassword[i++] = b;
|
||||
}
|
||||
final MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(saltPlusPassword);
|
||||
final byte[] digest = md.digest();
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final String sql = "update user set" + " password = ?, salt = ?" + " where id = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
try {
|
||||
statement.setBytes(1, digest);
|
||||
statement.setBytes(2, salt);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static User login(final String username, final String password)
|
||||
throws SQLException, NamingException, NoSuchAlgorithmException {
|
||||
Logger.log("Util.login " + username);
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final String sql = "select id, password, salt, display_name, can_write, username"
|
||||
+ " from user where username = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
try {
|
||||
statement.setString(1, username);
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
if (!resultSet.next()) {
|
||||
return null;
|
||||
}
|
||||
int i = 0;
|
||||
final int id = resultSet.getInt(++i);
|
||||
final byte[] passwordDigestBytes = resultSet.getBytes(++i);
|
||||
final byte[] salt = resultSet.getBytes(++i);
|
||||
final String displayName = resultSet.getString(++i);
|
||||
final boolean canWrite = resultSet.getBoolean(++i);
|
||||
final String actualUsername = resultSet.getString(++i);
|
||||
final byte[] passwordBytes = password.getBytes();
|
||||
final byte[] saltPlusPassword = new byte[salt.length + passwordBytes.length];
|
||||
i = 0;
|
||||
for (final byte b : salt) {
|
||||
saltPlusPassword[i++] = b;
|
||||
}
|
||||
for (final byte b : passwordBytes) {
|
||||
saltPlusPassword[i++] = b;
|
||||
}
|
||||
final MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
md.update(saltPlusPassword);
|
||||
final byte[] digest = md.digest();
|
||||
if (Arrays.equals(passwordDigestBytes, digest)) {
|
||||
Logger.log(username + " successfully logged in");
|
||||
return new User(id, actualUsername, displayName, canWrite);
|
||||
}
|
||||
Logger.log(username + " failed to log in");
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static User login(final String username)
|
||||
throws SQLException, NamingException, NoSuchAlgorithmException {
|
||||
Logger.log("Util.login " + username);
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final String sql = "select id, display_name, can_write, username"
|
||||
+ " from user where username = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
try {
|
||||
statement.setString(1, username);
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
if (!resultSet.next()) {
|
||||
return null;
|
||||
}
|
||||
int i = 0;
|
||||
final int id = resultSet.getInt(++i);
|
||||
final String displayName = resultSet.getString(++i);
|
||||
final boolean canWrite = resultSet.getBoolean(++i);
|
||||
final String actualUsername = resultSet.getString(++i);
|
||||
return new User(id, actualUsername, displayName, canWrite);
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
static int getMappingsCountFromDb() throws SQLException, NamingException {
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final String sql = "select count(*) from mapping";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
try {
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
if (resultSet.next()) {
|
||||
return resultSet.getInt(1);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static List<String> getAliasesFromDb(final String target) throws SQLException, NamingException {
|
||||
final List<String> list = new ArrayList<>();
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final String sql = "select distinct m.address" + " from mapping m"
|
||||
+ " inner join target t on t.id = m.target_id" + " where t.address = ?"
|
||||
+ " order by 1";
|
||||
Logger.log(sql);
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
try {
|
||||
Logger.log("target = " + target);
|
||||
statement.setString(1, target);
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
while (resultSet.next()) {
|
||||
list.add(resultSet.getString(1));
|
||||
}
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
Logger.log(String.format("returning %d aliases", Integer.valueOf(list.size())));
|
||||
return list;
|
||||
}
|
||||
|
||||
static List<Mapping> getMappingsFromDb() throws SQLException, NamingException {
|
||||
final List<Mapping> list = new ArrayList<>();
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final String sql = "select m.address, t.address, m.description, m.created, m.disabled"
|
||||
+ " from mapping m" + " inner join target t on t.id = m.target_id" + " order by 1";
|
||||
Logger.log(sql);
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
try {
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
while (resultSet.next()) {
|
||||
int i = 0;
|
||||
final String address = resultSet.getString(++i);
|
||||
final String target = resultSet.getString(++i);
|
||||
final String description = resultSet.getString(++i);
|
||||
final Timestamp created = resultSet.getTimestamp(++i);
|
||||
final boolean disabled = resultSet.getBoolean(++i);
|
||||
final Mapping mapping = new Mapping(address, target, description, created,
|
||||
disabled);
|
||||
list.add(mapping);
|
||||
Logger.debugLog(address);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
Logger.log(String.format("returning %d mappings", Integer.valueOf(list.size())));
|
||||
return list;
|
||||
}
|
||||
|
||||
static Mapping getMappingFromDb(final String address) throws SQLException, NamingException {
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final String sql = "select t.address, m.description, m.created, m.disabled"
|
||||
+ " from mapping m" + " inner join target t on t.id = m.target_id"
|
||||
+ " where m.address = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
try {
|
||||
statement.setString(1, address);
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
if (resultSet.next()) {
|
||||
int i = 0;
|
||||
final String target = resultSet.getString(++i);
|
||||
final String description = resultSet.getString(++i);
|
||||
final Timestamp created = resultSet.getTimestamp(++i);
|
||||
final boolean disabled = resultSet.getBoolean(++i);
|
||||
return new Mapping(address, target, description, created, disabled);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Target getTarget(final Connection connection, final String address)
|
||||
throws SQLException {
|
||||
if (address == null) {
|
||||
throw new IllegalArgumentException("address may not be null");
|
||||
}
|
||||
Target target = null;
|
||||
boolean targetFound = false;
|
||||
try {
|
||||
target = getTargetFromDb(connection, address);
|
||||
targetFound = true;
|
||||
}
|
||||
catch (final TargetNotFoundException e) {
|
||||
}
|
||||
if (!targetFound) {
|
||||
target = addTargetToDb(connection, address, "");
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
static void addMappingToDb(final Mapping mapping) throws SQLException, NamingException {
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final Target target = getTarget(connection, mapping.getTarget());
|
||||
final String sql = "insert into mapping"
|
||||
+ " (address, target_id, description, created, disabled)"
|
||||
+ " values (?, ?, ?, ?, ?)";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql,
|
||||
PreparedStatement.RETURN_GENERATED_KEYS);
|
||||
try {
|
||||
int i = 0;
|
||||
statement.setString(++i, mapping.getAddress());
|
||||
statement.setInt(++i, target.getId());
|
||||
statement.setString(++i, mapping.getDescription());
|
||||
final Date created = mapping.getDate();
|
||||
if (created == null) {
|
||||
statement.setNull(++i, Types.TIMESTAMP);
|
||||
}
|
||||
else {
|
||||
statement.setTimestamp(++i, new Timestamp(created.getTime()));
|
||||
}
|
||||
statement.setBoolean(++i, mapping.isDisabled());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
static void updateMappingToDb(final Mapping mapping) throws SQLException, NamingException {
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final Target target = getTarget(connection, mapping.getTarget());
|
||||
final String sql = "update mapping set"
|
||||
+ " target_id = ?, description = ?, created = ?, disabled = ?"
|
||||
+ " where address = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql,
|
||||
PreparedStatement.RETURN_GENERATED_KEYS);
|
||||
try {
|
||||
int i = 0;
|
||||
statement.setInt(++i, target.getId());
|
||||
statement.setString(++i, mapping.getDescription());
|
||||
final Date created = mapping.getDate();
|
||||
if (created == null) {
|
||||
statement.setNull(++i, Types.TIMESTAMP);
|
||||
}
|
||||
else {
|
||||
statement.setTimestamp(++i, new Timestamp(created.getTime()));
|
||||
}
|
||||
statement.setBoolean(++i, mapping.isDisabled());
|
||||
statement.setString(++i, mapping.getAddress());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
static int addHistoryToDb(final Mapping mapping) throws SQLException, NamingException {
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
final Target target = getTarget(connection, mapping.getTarget());
|
||||
final String sql = "insert into history"
|
||||
+ " (address, target_id, description, created, changed, disabled)"
|
||||
+ " values (?, ?, ?, ?, ?, ?)";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql,
|
||||
PreparedStatement.RETURN_GENERATED_KEYS);
|
||||
try {
|
||||
int i = 0;
|
||||
statement.setString(++i, mapping.getAddress());
|
||||
statement.setInt(++i, target.getId());
|
||||
statement.setString(++i, mapping.getDescription());
|
||||
final Date created = mapping.getDate();
|
||||
if (created == null) {
|
||||
statement.setNull(++i, Types.TIMESTAMP);
|
||||
}
|
||||
else {
|
||||
statement.setTimestamp(++i, new Timestamp(created.getTime()));
|
||||
}
|
||||
statement.setTimestamp(++i, new Timestamp(System.currentTimeMillis()));
|
||||
statement.setBoolean(++i, mapping.isDisabled());
|
||||
final int rowCount = statement.executeUpdate();
|
||||
if (rowCount == 0) {
|
||||
throw new RuntimeException("Failed to insert new mapping");
|
||||
}
|
||||
final ResultSet resultSet = statement.getGeneratedKeys();
|
||||
try {
|
||||
if (resultSet.next()) {
|
||||
return resultSet.getInt(1);
|
||||
}
|
||||
throw new RuntimeException("Failed to get new mapping id");
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static class TargetNotFoundException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
private static Target getTargetFromDb(final Connection connection, final String address)
|
||||
throws SQLException, TargetNotFoundException {
|
||||
if (address == null) {
|
||||
throw new IllegalArgumentException("address may not be null");
|
||||
}
|
||||
final String sql = "select id, description, created from target where address = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
try {
|
||||
statement.setString(1, address);
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
if (resultSet.next()) {
|
||||
final int id = resultSet.getInt(1);
|
||||
final String description = resultSet.getString(2);
|
||||
final Timestamp created = resultSet.getTimestamp(3);
|
||||
return new Target(id, address, description, created);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
throw new TargetNotFoundException();
|
||||
}
|
||||
|
||||
private static Target addTargetToDb(final Connection connection, final String address,
|
||||
final String description) throws SQLException {
|
||||
if (address == null) {
|
||||
throw new IllegalArgumentException("address may not be null");
|
||||
}
|
||||
final Timestamp created = new Timestamp(System.currentTimeMillis());
|
||||
final String sql = "insert into target (address, description, created)"
|
||||
+ " values (?, ?, ?)";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql,
|
||||
PreparedStatement.RETURN_GENERATED_KEYS);
|
||||
try {
|
||||
int i = 0;
|
||||
statement.setString(++i, address);
|
||||
statement.setString(++i, description);
|
||||
statement.setTimestamp(++i, created);
|
||||
final int rowCount = statement.executeUpdate();
|
||||
if (rowCount == 0) {
|
||||
throw new RuntimeException("Failed to insert new target");
|
||||
}
|
||||
final ResultSet resultSet = statement.getGeneratedKeys();
|
||||
try {
|
||||
if (resultSet.next()) {
|
||||
final int id = resultSet.getInt(1);
|
||||
return new Target(id, address, description, created);
|
||||
}
|
||||
throw new RuntimeException("Failed to get new target id");
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Target> getTargetsForUser(final User user)
|
||||
throws SQLException, NamingException {
|
||||
final Connection connection = Configuration.INSTANCE.getConnection();
|
||||
try {
|
||||
return getTargetsForUser(connection, user);
|
||||
}
|
||||
finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Target> getTargetsForUser(final Connection connection, final User user)
|
||||
throws SQLException {
|
||||
final List<Target> list = new ArrayList<>();
|
||||
final String sql = "select t.id, t.address, t.description, t.created"
|
||||
+ " from target t inner join user_target ut on t.id = ut.target_id"
|
||||
+ " where ut.user_id = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
try {
|
||||
statement.setInt(1, user.getId());
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
while (resultSet.next()) {
|
||||
int i = 0;
|
||||
final int id = resultSet.getInt(++i);
|
||||
final String address = resultSet.getString(++i);
|
||||
final String description = resultSet.getString(++i);
|
||||
final Date created = resultSet.getDate(++i);
|
||||
final Target target = new Target(id, address, description, created);
|
||||
list.add(target);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
statement.close();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void logout(final HttpSession session) {
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
session.removeAttribute("email-session");
|
||||
}
|
||||
|
||||
public static User identify(final HttpSession session) {
|
||||
if (session == null) {
|
||||
return null;
|
||||
}
|
||||
final Object object = session.getAttribute("email-session");
|
||||
if (object == null || !(object instanceof Session)) {
|
||||
return null;
|
||||
}
|
||||
final Session appSession = (Session) object;
|
||||
return appSession.getUser();
|
||||
}
|
||||
|
||||
public interface SaveMappingsResult {
|
||||
int getExitValue();
|
||||
|
||||
String getOutput();
|
||||
|
||||
String getError();
|
||||
}
|
||||
|
||||
static String streamToString(final InputStream inputStream) throws IOException {
|
||||
final Reader reader = new InputStreamReader(inputStream);
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final char[] buffer = new char[0x1000];
|
||||
int charsRead = reader.read(buffer);
|
||||
while (charsRead >= 0) {
|
||||
sb.append(buffer, 0, charsRead);
|
||||
charsRead = reader.read(buffer);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static boolean addressExists(final String address)
|
||||
throws SQLException, NamingException, IOException {
|
||||
return GlobalCache.INSTANCE.addressExists(address);
|
||||
}
|
||||
|
||||
static void addMapping(final Mapping mapping)
|
||||
throws IOException, SQLException, NamingException {
|
||||
GlobalCache.INSTANCE.addMapping(mapping);
|
||||
}
|
||||
|
||||
static void updateMapping(final Mapping mapping)
|
||||
throws IOException, SQLException, NamingException {
|
||||
GlobalCache.INSTANCE.updateMapping(mapping);
|
||||
}
|
||||
|
||||
static void addHistory(final Mapping mapping)
|
||||
throws IOException, SQLException, NamingException {
|
||||
GlobalCache.INSTANCE.addHistory(mapping);
|
||||
}
|
||||
|
||||
public static Mapping getMapping(final String address)
|
||||
throws IOException, SQLException, NamingException {
|
||||
return GlobalCache.INSTANCE.getMapping(address);
|
||||
}
|
||||
|
||||
static SaveMappingsResult saveMappings()
|
||||
throws IOException, InterruptedException, SQLException, NamingException {
|
||||
return GlobalCache.INSTANCE.writeVirtualFile();
|
||||
}
|
||||
|
||||
public static List<Mapping> getMappings() throws IOException, SQLException, NamingException {
|
||||
return GlobalCache.INSTANCE.getMappings();
|
||||
}
|
||||
|
||||
public static List<String> getAliases(final String target)
|
||||
throws IOException, SQLException, NamingException {
|
||||
return GlobalCache.INSTANCE.getAliases(target);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue