forked from I2P_Developers/i2p.i2p
HostTxtEntry: Fixups for use by i2ptunnel
i2ptunnel: Add new registration authentication page - Remove old, unused hostname signature generation PrivateKeyFile: Ensure initialization before returning private keys
This commit is contained in:
@ -71,6 +71,7 @@ public class EditBean extends IndexBean {
|
||||
return _helper.getPrivateKeyFile(tunnel);
|
||||
}
|
||||
|
||||
/****
|
||||
public String getNameSignature(int tunnel) {
|
||||
String spoof = getSpoofedHost(tunnel);
|
||||
if (spoof.length() <= 0)
|
||||
@ -100,6 +101,26 @@ public class EditBean extends IndexBean {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* @since 0.9.26
|
||||
* @return key or null
|
||||
*/
|
||||
public SigningPrivateKey getSigningPrivateKey(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun == null)
|
||||
return null;
|
||||
String keyFile = tun.getPrivKeyFile();
|
||||
if (keyFile != null && keyFile.trim().length() > 0) {
|
||||
File f = new File(keyFile);
|
||||
if (!f.isAbsolute())
|
||||
f = new File(_context.getConfigDir(), keyFile);
|
||||
PrivateKeyFile pkf = new PrivateKeyFile(f);
|
||||
return pkf.getSigningPrivKey();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean startAutomatically(int tunnel) {
|
||||
return _helper.shouldStartAutomatically(tunnel);
|
||||
|
@ -228,6 +228,9 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
|
||||
<textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Read Only: Local Destination (if known)" wrap="off" spellcheck="false"><%=editBean.getDestinationBase64(curTunnel)%></textarea>
|
||||
</div>
|
||||
|
||||
<%
|
||||
/******
|
||||
%>
|
||||
<% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
|
||||
String sig = editBean.getNameSignature(curTunnel);
|
||||
if (sig.length() > 0) {
|
||||
@ -240,6 +243,7 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
|
||||
<%
|
||||
} // sig
|
||||
} // type
|
||||
****/
|
||||
|
||||
String b64 = editBean.getDestinationBase64(curTunnel);
|
||||
if (!"".equals(b64)) {
|
||||
@ -256,11 +260,14 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
|
||||
<a class="control" title="<%=intl._t("Generate QR Code")%>" href="/imagegen/qr?s=320&t=<%=name%>&c=http%3a%2f%2f<%=name%>%2f%3fi2paddresshelper%3d<%=b64%>" target="_top"><%=intl._t("Generate QR Code")%></a>
|
||||
</label>
|
||||
<a class="control" href="/susidns/addressbook.jsp?book=private&hostname=<%=name%>&destination=<%=b64%>#add"><%=intl._t("Add to local addressbook")%></a>
|
||||
|
||||
<a class="control" href="register?tunnel=<%=curTunnel%>"><%=intl._t("Registration Authentication")%></a>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
<label> </label>
|
||||
<span class="comment"><%=intl._t("Set name with .i2p suffix to enable QR code generation")%></span>
|
||||
<span class="comment"><%=intl._t("Set name with .i2p suffix to enable registration authentication")%></span>
|
||||
<%
|
||||
} // name
|
||||
%>
|
||||
|
191
apps/i2ptunnel/jsp/register.jsp
Normal file
191
apps/i2ptunnel/jsp/register.jsp
Normal file
@ -0,0 +1,191 @@
|
||||
<%@page contentType="text/html" import="net.i2p.i2ptunnel.web.EditBean,net.i2p.client.naming.HostTxtEntry,net.i2p.data.SigningPrivateKey,net.i2p.util.OrderedProperties"
|
||||
%><%@page trimDirectiveWhitespaces="true"
|
||||
%><?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<%
|
||||
/* right now using EditBean instead of IndexBean for getSpoofedHost() */
|
||||
/* but might want to POST to it anyway ??? */
|
||||
%>
|
||||
<jsp:useBean class="net.i2p.i2ptunnel.web.EditBean" id="editBean" scope="request" />
|
||||
<jsp:useBean class="net.i2p.i2ptunnel.web.Messages" id="intl" scope="request" />
|
||||
<% String tun = request.getParameter("tunnel");
|
||||
int curTunnel = -1;
|
||||
if (tun != null) {
|
||||
try {
|
||||
curTunnel = Integer.parseInt(tun);
|
||||
} catch (NumberFormatException nfe) {
|
||||
curTunnel = -1;
|
||||
}
|
||||
}
|
||||
%>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title><%=intl._t("Hidden Services Manager")%> - <%=intl._t("Registration Helper")%></title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
|
||||
<link href="/themes/console/images/favicon.ico" type="image/x-icon" rel="shortcut icon" />
|
||||
|
||||
<% if (editBean.allowCSS()) {
|
||||
%><link rel="icon" href="<%=editBean.getTheme()%>images/favicon.ico" />
|
||||
<link href="<%=editBean.getTheme()%>default.css" rel="stylesheet" type="text/css" />
|
||||
<link href="<%=editBean.getTheme()%>i2ptunnel.css" rel="stylesheet" type="text/css" />
|
||||
<% }
|
||||
%>
|
||||
<style type='text/css'>
|
||||
input.default { width: 1px; height: 1px; visibility: hidden; }
|
||||
</style>
|
||||
</head>
|
||||
<body id="tunnelEditPage">
|
||||
<div id="pageHeader">
|
||||
</div>
|
||||
<%
|
||||
|
||||
if (editBean.isInitialized()) {
|
||||
|
||||
%>
|
||||
<form method="post" action="authenticate">
|
||||
|
||||
<div id="tunnelEditPanel" class="panel">
|
||||
<div class="header">
|
||||
<%
|
||||
String tunnelTypeName;
|
||||
String tunnelType;
|
||||
boolean valid = false;
|
||||
if (curTunnel >= 0) {
|
||||
tunnelTypeName = editBean.getTunnelType(curTunnel);
|
||||
tunnelType = editBean.getInternalType(curTunnel);
|
||||
%><h4><%=intl._t("Registration Helper")%></h4><%
|
||||
} else {
|
||||
tunnelTypeName = "new";
|
||||
tunnelType = "new";
|
||||
%><h4>Fail</h4><p>Tunnel not found</p><%
|
||||
}
|
||||
String b64 = editBean.getDestinationBase64(curTunnel);
|
||||
String name = editBean.getSpoofedHost(curTunnel);
|
||||
if (name == null || name.equals(""))
|
||||
name = editBean.getTunnelName(curTunnel);
|
||||
%>
|
||||
<input type="hidden" name="tunnel" value="<%=curTunnel%>" />
|
||||
<input type="hidden" name="nonce" value="<%=net.i2p.i2ptunnel.web.IndexBean.getNextNonce()%>" />
|
||||
<input type="hidden" name="type" value="<%=tunnelType%>" />
|
||||
<input type="submit" class="default" name="action" value="Save changes" />
|
||||
</div>
|
||||
<%
|
||||
if (!"new".equals(tunnelType)) {
|
||||
%>
|
||||
<div class="separator">
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<div id="nameField" class="rowItem">
|
||||
<label for="name" accesskey="N">
|
||||
<%=intl._t("Name")%>(<span class="accessKey">N</span>):
|
||||
</label>
|
||||
<span class="text"><%=editBean.getTunnelName(curTunnel)%></span>
|
||||
</div>
|
||||
<div id="typeField" class="rowItem">
|
||||
<label><%=intl._t("Type")%>:</label>
|
||||
<span class="text"><%=tunnelTypeName%></span>
|
||||
</div>
|
||||
|
||||
<%
|
||||
if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
|
||||
%><div id="websiteField" class="rowItem">
|
||||
<label for="spoofedHost" accesskey="W">
|
||||
<%=intl._t("Website name")%>(<span class="accessKey">W</span>):
|
||||
</label>
|
||||
<span class="text"><%=editBean.getSpoofedHost(curTunnel)%></span>
|
||||
</div>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
<div id="destinationField" class="rowItem">
|
||||
<label for="localDestination" accesskey="L">
|
||||
<%=intl._t("Local destination")%>(<span class="accessKey">L</span>):
|
||||
</label>
|
||||
<textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Read Only: Local Destination (if known)" wrap="off" spellcheck="false"><%=editBean.getDestinationBase64(curTunnel)%></textarea>
|
||||
</div>
|
||||
<div class="subdivider">
|
||||
<hr />
|
||||
</div>
|
||||
<%
|
||||
if (b64 == null || b64.length() < 516) {
|
||||
%><%=intl._t("Local destination is not available. Start the tunnel.")%><%
|
||||
} else if (name == null || name.equals("") || name.contains(" ") || !name.endsWith(".i2p")) {
|
||||
if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
|
||||
%><%=intl._t("To enable registration verification, edit tunnel and set name (or website name) to a valid host name ending in '.i2p'")%><%
|
||||
} else {
|
||||
%><%=intl._t("To enable registration verification, edit tunnel and set name to a valid host name ending in '.i2p'")%><%
|
||||
}
|
||||
} else {
|
||||
SigningPrivateKey spk = editBean.getSigningPrivateKey(curTunnel);
|
||||
if (spk == null) {
|
||||
%><%=intl._t("Destination signing key is not available. Start the tunnel.")%><%
|
||||
} else {
|
||||
valid = true;
|
||||
OrderedProperties props = new OrderedProperties();
|
||||
HostTxtEntry he = new HostTxtEntry(name, b64, props);
|
||||
he.sign(spk);
|
||||
%><div id="destinationField" class="rowItem">
|
||||
<label><%=intl._t("Authentication Strings")%>:</label>
|
||||
<span class="text"><%=intl._t("Select and copy the entire contents of the appropriate box")%></span>
|
||||
</div>
|
||||
<div id="sigField" class="rowItem">
|
||||
<label for="signature">
|
||||
<%=intl._t("Authentication for adding host")%>
|
||||
</label>
|
||||
<textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.writeProps(out); %></textarea>
|
||||
</div>
|
||||
<%
|
||||
props.remove(HostTxtEntry.PROP_SIG);
|
||||
props.setProperty(HostTxtEntry.PROP_ACTION, HostTxtEntry.ACTION_REMOVE);
|
||||
he.signRemove(spk);
|
||||
%><div id="sigField" class="rowItem">
|
||||
<label for="signature">
|
||||
<%=intl._t("Authentication for removing host")%>
|
||||
</label>
|
||||
<textarea rows="1" style="height: 3em;" cols="60" readonly="readonly" id="localDestination" title="Copy and paste this to the registration site" wrap="off" spellcheck="false"><% he.writeRemove(out); %></textarea>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="footer">
|
||||
</div>
|
||||
<%
|
||||
} // spk != null
|
||||
} // valid b64 and name
|
||||
} // !"new".equals(tunnelType)
|
||||
if (!valid) {
|
||||
%><a href="edit?tunnel=<%=curTunnel%>"><%=intl._t("Go back and edit the tunnel")%></a><%
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
|
||||
|
||||
<%
|
||||
if (false && valid) {
|
||||
%>
|
||||
<div id="globalOperationsPanel" class="panel">
|
||||
<div class="header"></div>
|
||||
<div class="footer">
|
||||
<div class="toolbox">
|
||||
<input type="hidden" value="true" name="removeConfirm" />
|
||||
<button id="controlCancel" class="control" type="submit" name="action" value="" title="Cancel"><%=intl._t("Cancel")%></button>
|
||||
<button id="controlSave" accesskey="S" class="control" type="submit" name="action" value="authenticate" title="Generate Authentication"><%=intl._t("Generate")%>(<span class="accessKey">S</span>)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%
|
||||
} // valid
|
||||
%>
|
||||
</form>
|
||||
<div id="pageFooter">
|
||||
</div>
|
||||
<%
|
||||
|
||||
} else {
|
||||
%>Tunnels are not initialized yet, please reload in two minutes.<%
|
||||
} // isInitialized()
|
||||
|
||||
%>
|
||||
</body>
|
||||
</html>
|
@ -32,6 +32,11 @@
|
||||
<url-pattern>/wizard</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>net.i2p.i2ptunnel.jsp.register_jsp</servlet-name>
|
||||
<url-pattern>/register</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- this webapp doesn't actually use sessions or cookies -->
|
||||
<session-config>
|
||||
<session-timeout>
|
||||
|
@ -139,7 +139,7 @@ public class HostTxtEntry {
|
||||
out.write(KV_SEPARATOR);
|
||||
out.write(dest);
|
||||
}
|
||||
writeProps(out, false, false);
|
||||
writeProps(out);
|
||||
out.newLine();
|
||||
}
|
||||
|
||||
@ -149,21 +149,38 @@ public class HostTxtEntry {
|
||||
* Includes newline.
|
||||
* Must have been constructed with non-null properties.
|
||||
*/
|
||||
public void writeRemove(BufferedWriter out) throws IOException {
|
||||
public void writeRemoveLine(BufferedWriter out) throws IOException {
|
||||
writeRemove(out);
|
||||
out.newLine();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write as a "remove" line #!dest=dest#name=name#k1=v1#sig=sig...]
|
||||
* This works whether constructed with name and dest, or just properties.
|
||||
* Does not include newline.
|
||||
* Must have been constructed with non-null properties.
|
||||
*/
|
||||
public void writeRemove(Writer out) throws IOException {
|
||||
if (props == null)
|
||||
throw new IllegalStateException();
|
||||
if (name != null && dest != null) {
|
||||
props.setProperty(PROP_NAME, name);
|
||||
props.setProperty(PROP_DEST, dest);
|
||||
}
|
||||
writeProps(out, false, false);
|
||||
out.newLine();
|
||||
writeProps(out);
|
||||
if (name != null && dest != null) {
|
||||
props.remove(PROP_NAME);
|
||||
props.remove(PROP_DEST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the props part (if any) only, without newline
|
||||
*/
|
||||
public void writeProps(Writer out) throws IOException {
|
||||
writeProps(out, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the props part (if any) only, without newline
|
||||
*/
|
||||
@ -346,20 +363,23 @@ public class HostTxtEntry {
|
||||
|
||||
/**
|
||||
* Sign and set the "sig" property
|
||||
* Must have been constructed with non-null properties.
|
||||
*/
|
||||
private void sign(SigningPrivateKey spk) {
|
||||
public void sign(SigningPrivateKey spk) {
|
||||
signIt(spk, PROP_SIG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign and set the "oldsig" property
|
||||
* Must have been constructed with non-null properties.
|
||||
*/
|
||||
private void signInner(SigningPrivateKey spk) {
|
||||
public void signInner(SigningPrivateKey spk) {
|
||||
signIt(spk, PROP_OLDSIG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign as a "remove" line #!dest=dest#name=name#k1=v1#sig=sig...]
|
||||
* Must have been constructed with non-null properties.
|
||||
*/
|
||||
public void signRemove(SigningPrivateKey spk) {
|
||||
if (props == null)
|
||||
@ -370,7 +390,7 @@ public class HostTxtEntry {
|
||||
props.setProperty(PROP_DEST, dest);
|
||||
StringWriter buf = new StringWriter(1024);
|
||||
try {
|
||||
writeProps(buf, false, false);
|
||||
writeProps(buf);
|
||||
} catch (IOException ioe) {
|
||||
throw new IllegalStateException(ioe);
|
||||
}
|
||||
@ -395,7 +415,7 @@ public class HostTxtEntry {
|
||||
buf.append(KV_SEPARATOR);
|
||||
buf.append(dest);
|
||||
try {
|
||||
writeProps(buf, false, false);
|
||||
writeProps(buf);
|
||||
} catch (IOException ioe) {
|
||||
throw new IllegalStateException(ioe);
|
||||
}
|
||||
@ -490,7 +510,7 @@ public class HostTxtEntry {
|
||||
//out.write("Remove entry:\n");
|
||||
sw = new StringWriter(1024);
|
||||
buf = new BufferedWriter(sw);
|
||||
he.writeRemove(buf);
|
||||
he.writeRemoveLine(buf);
|
||||
buf.flush();
|
||||
out.write(sw.toString());
|
||||
out.flush();
|
||||
|
@ -455,11 +455,29 @@ public class PrivateKeyFile {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null on error or if not initialized
|
||||
*/
|
||||
public PrivateKey getPrivKey() {
|
||||
try {
|
||||
// call this to force initialization
|
||||
getDestination();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return this.privKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null on error or if not initialized
|
||||
*/
|
||||
public SigningPrivateKey getSigningPrivKey() {
|
||||
try {
|
||||
// call this to force initialization
|
||||
getDestination();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return this.signingPrivKey;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user