forked from I2P_Developers/i2p.i2p
merge of '0738aeef8a1d4e9ca82dc5ba0077d83a57c47f81'
and '9625ea3e96d57df74bc62018bf64230a22c49ce0'
This commit is contained in:
@@ -81,7 +81,7 @@
|
||||
|
||||
<target name="war" depends="precompilejsp, bundle">
|
||||
<war destfile="build/i2ptunnel.war" webxml="../jsp/web-out.xml"
|
||||
basedir="../jsp/" excludes="web.xml, **/*.java, *.jsp">
|
||||
basedir="../jsp/" excludes="web.xml, web-fragment.xml, web-out.xml, **/*.java, *.jsp">
|
||||
</war>
|
||||
</target>
|
||||
|
||||
|
@@ -24,6 +24,9 @@ import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* This does the transparent gzip decompression on the client side.
|
||||
* Extended in I2PTunnelHTTPServer to do the compression on the server side.
|
||||
*
|
||||
* Simple stream for delivering an HTTP response to
|
||||
* the client, trivially filtered to make sure "Connection: close"
|
||||
* is always in the response. Perhaps add transparent handling of the
|
||||
@@ -33,29 +36,27 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private ByteCache _cache;
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
protected ByteArray _headerBuffer;
|
||||
private boolean _headerWritten;
|
||||
private byte _buf1[];
|
||||
private final byte _buf1[];
|
||||
protected boolean _gzip;
|
||||
private long _dataWritten;
|
||||
private InternalGZIPInputStream _in;
|
||||
private static final int CACHE_SIZE = 8*1024;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(8, CACHE_SIZE);
|
||||
// OOM DOS prevention
|
||||
private static final int MAX_HEADER_SIZE = 64*1024;
|
||||
|
||||
public HTTPResponseOutputStream(OutputStream raw) {
|
||||
super(raw);
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*60*1000 });
|
||||
_log = _context.logManager().getLog(getClass());
|
||||
_cache = ByteCache.getInstance(8, CACHE_SIZE);
|
||||
_headerBuffer = _cache.acquire();
|
||||
_headerWritten = false;
|
||||
_gzip = false;
|
||||
_dataWritten = 0;
|
||||
_buf1 = new byte[1];
|
||||
}
|
||||
|
||||
@@ -96,14 +97,20 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
/** grow (and free) the buffer as necessary */
|
||||
private void ensureCapacity() {
|
||||
/**
|
||||
* grow (and free) the buffer as necessary
|
||||
* @throws IOException if the headers are too big
|
||||
*/
|
||||
private void ensureCapacity() throws IOException {
|
||||
if (_headerBuffer.getValid() >= MAX_HEADER_SIZE)
|
||||
throw new IOException("Max header size exceeded: " + MAX_HEADER_SIZE);
|
||||
if (_headerBuffer.getValid() + 1 >= _headerBuffer.getData().length) {
|
||||
int newSize = (int)(_headerBuffer.getData().length * 1.5);
|
||||
ByteArray newBuf = new ByteArray(new byte[newSize]);
|
||||
System.arraycopy(_headerBuffer.getData(), 0, newBuf.getData(), 0, _headerBuffer.getValid());
|
||||
newBuf.setValid(_headerBuffer.getValid());
|
||||
newBuf.setOffset(0);
|
||||
// if we changed the ByteArray size, don't put it back in the cache
|
||||
if (_headerBuffer.getData().length == CACHE_SIZE)
|
||||
_cache.release(_headerBuffer);
|
||||
_headerBuffer = newBuf;
|
||||
@@ -219,7 +226,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
//out.flush();
|
||||
PipedInputStream pi = new PipedInputStream();
|
||||
PipedOutputStream po = new PipedOutputStream(pi);
|
||||
new I2PAppThread(new Pusher(pi, out), "HTTP decompresser").start();
|
||||
new I2PAppThread(new Pusher(pi, out), "HTTP decompressor").start();
|
||||
out = po;
|
||||
}
|
||||
|
||||
@@ -231,13 +238,13 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
_out = out;
|
||||
}
|
||||
public void run() {
|
||||
OutputStream to = null;
|
||||
_in = null;
|
||||
long start = System.currentTimeMillis();
|
||||
long written = 0;
|
||||
ByteArray ba = null;
|
||||
try {
|
||||
_in = new InternalGZIPInputStream(_inRaw);
|
||||
byte buf[] = new byte[8192];
|
||||
ba = _cache.acquire();
|
||||
byte buf[] = ba.getData();
|
||||
int read = -1;
|
||||
while ( (read = _in.read(buf)) != -1) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -251,6 +258,8 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error decompressing: " + written + ", " + (_in != null ? _in.getTotalRead() + "/" + _in.getTotalExpanded() : ""), ioe);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
_log.error("OOM in HTTP Decompressor", oom);
|
||||
} finally {
|
||||
if (_log.shouldLog(Log.WARN) && (_in != null))
|
||||
_log.warn("After decompression, written=" + written +
|
||||
@@ -259,23 +268,26 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
+ ", expanded=" + _in.getTotalExpanded() + ", remaining=" + _in.getRemaining()
|
||||
+ ", finished=" + _in.getFinished()
|
||||
: ""));
|
||||
if (ba != null)
|
||||
_cache.release(ba);
|
||||
if (_out != null) try {
|
||||
_out.close();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
double compressed = (_in != null ? _in.getTotalRead() : 0);
|
||||
double expanded = (_in != null ? _in.getTotalExpanded() : 0);
|
||||
double ratio = 0;
|
||||
if (expanded > 0)
|
||||
ratio = compressed/expanded;
|
||||
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), end-start);
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, end-start);
|
||||
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, end-start);
|
||||
if (compressed > 0 && expanded > 0) {
|
||||
// only update the stats if we did something
|
||||
double ratio = compressed/expanded;
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), 0);
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, 0);
|
||||
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** just a wrapper to provide stats for debugging */
|
||||
private static class InternalGZIPInputStream extends GZIPInputStream {
|
||||
public InternalGZIPInputStream(InputStream in) throws IOException {
|
||||
super(in);
|
||||
@@ -294,6 +306,12 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From Inflater javadoc:
|
||||
* Returns the total number of bytes remaining in the input buffer. This can be used to find out
|
||||
* what bytes still remain in the input buffer after decompression has finished.
|
||||
*/
|
||||
public long getRemaining() {
|
||||
try {
|
||||
return super.inf.getRemaining();
|
||||
|
@@ -8,7 +8,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
@@ -110,7 +109,7 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
}
|
||||
if (size == 1) // skip the rand in the most common case
|
||||
return dests.get(0);
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
int index = _context.random().nextInt(size);
|
||||
return dests.get(index);
|
||||
}
|
||||
}
|
||||
|
@@ -146,7 +146,7 @@ public class I2PTunnelConnectClient extends I2PTunnelClientBase implements Runna
|
||||
int size = _proxyList.size();
|
||||
if (size <= 0)
|
||||
return null;
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
int index = _context.random().nextInt(size);
|
||||
return _proxyList.get(index);
|
||||
}
|
||||
}
|
||||
|
@@ -205,7 +205,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
l.log("Proxy list is empty - no outproxy available");
|
||||
return null;
|
||||
}
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
int index = _context.random().nextInt(size);
|
||||
String proxy = (String)proxyList.get(index);
|
||||
return proxy;
|
||||
}
|
||||
@@ -626,6 +626,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
//line = "From: i2p";
|
||||
line = null;
|
||||
continue; // completely strip the line
|
||||
} else if (lowercaseLine.startsWith("authorization: ntlm ")) {
|
||||
// Block Windows NTLM after 401
|
||||
line = null;
|
||||
continue;
|
||||
} else if (lowercaseLine.startsWith("proxy-authorization: ntlm ")) {
|
||||
// Block Windows NTLM after 407
|
||||
line = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -808,7 +816,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
* @return non-null
|
||||
*/
|
||||
private byte[] getErrorPage(String base, byte[] backup) {
|
||||
return getErrorPage(getTunnel().getContext(), base, backup);
|
||||
return getErrorPage(_context, base, backup);
|
||||
}
|
||||
|
||||
private static byte[] getErrorPage(I2PAppContext ctx, String base, byte[] backup) {
|
||||
|
@@ -290,6 +290,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
}
|
||||
}
|
||||
|
||||
/** just a wrapper to provide stats for debugging */
|
||||
private static class InternalGZIPOutputStream extends GZIPOutputStream {
|
||||
public InternalGZIPOutputStream(OutputStream target) throws IOException {
|
||||
super(target);
|
||||
|
@@ -9,7 +9,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
@@ -124,7 +123,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
if (size == 1) // skip the rand in the most common case
|
||||
return dests.get(0);
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
int index = _context.random().nextInt(size);
|
||||
return dests.get(index);
|
||||
}
|
||||
|
||||
@@ -182,6 +181,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
outmsg=outmsg+"\r\n"; // rfc1459 sec. 2.3
|
||||
output.write(outmsg.getBytes("ISO-8859-1"));
|
||||
// probably doesn't do much but can't hurt
|
||||
output.flush();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("inbound BLOCKED: "+inmsg);
|
||||
@@ -257,6 +258,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
outmsg=outmsg+"\r\n"; // rfc1459 sec. 2.3
|
||||
output.write(outmsg.getBytes("ISO-8859-1"));
|
||||
// save 250 ms in streaming
|
||||
output.flush();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("outbound BLOCKED: "+"\""+inmsg+"\"");
|
||||
|
@@ -129,7 +129,14 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
||||
// do NOT flush here, it will block and then onTimeout.run() won't happen on fail.
|
||||
// But if we don't flush, then we have to wait for the connectDelay timer to fire
|
||||
// in i2p socket? To be researched and/or fixed.
|
||||
//i2pout.flush();
|
||||
//
|
||||
// AS OF 0.8.1, MessageOutputStream.flush() is fixed to only wait for accept,
|
||||
// not for "completion" (i.e. an ACK from the far end).
|
||||
// So we now get a fast return from flush(), and can do it here to save 250 ms.
|
||||
// To make sure we are under the initial window size and don't hang waiting for accept,
|
||||
// only flush if it fits in one message.
|
||||
if (initialI2PData.length <= 1730) // ConnectionOptions.DEFAULT_MAX_MESSAGE_SIZE
|
||||
i2pout.flush();
|
||||
}
|
||||
}
|
||||
if (initialSocketData != null) {
|
||||
|
@@ -18,6 +18,7 @@ import net.i2p.data.Base32;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
/**
|
||||
* Coordinate the runtime operation and configuration of a tunnel.
|
||||
@@ -89,7 +90,7 @@ public class TunnelController implements Logging {
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(keyFile);
|
||||
fos = new SecureFileOutputStream(keyFile);
|
||||
Destination dest = client.createDestination(fos);
|
||||
String destStr = dest.toBase64();
|
||||
log("Private key created and saved in " + keyFile.getAbsolutePath());
|
||||
|
@@ -20,6 +20,7 @@ import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
/**
|
||||
* Coordinate a set of tunnels within the JVM, loading and storing their config
|
||||
@@ -255,7 +256,7 @@ public class TunnelControllerGroup {
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(cfgFile);
|
||||
fos = new SecureFileOutputStream(cfgFile);
|
||||
fos.write(buf.toString().getBytes("UTF-8"));
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Config written to " + cfgFile.getPath());
|
||||
|
@@ -13,6 +13,11 @@ import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.PrivateKeyFile;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.i2ptunnel.TunnelController;
|
||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||
|
||||
@@ -68,6 +73,31 @@ public class EditBean extends IndexBean {
|
||||
return "i2ptunnel" + tunnel + "-privKeys.dat";
|
||||
}
|
||||
|
||||
public String getNameSignature(int tunnel) {
|
||||
String spoof = getSpoofedHost(tunnel);
|
||||
if (spoof.length() <= 0)
|
||||
return "";
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun == null)
|
||||
return "";
|
||||
String keyFile = tun.getPrivKeyFile();
|
||||
if (keyFile != null && keyFile.trim().length() > 0) {
|
||||
PrivateKeyFile pkf = new PrivateKeyFile(keyFile);
|
||||
try {
|
||||
Destination d = pkf.getDestination();
|
||||
if (d == null)
|
||||
return "";
|
||||
SigningPrivateKey privKey = pkf.getSigningPrivKey();
|
||||
if (privKey == null)
|
||||
return "";
|
||||
//System.err.println("Signing " + spoof + " with " + Base64.encode(privKey.getData()));
|
||||
Signature sig = _context.dsa().sign(spoof.getBytes("UTF-8"), privKey);
|
||||
return Base64.encode(sig.getData());
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public boolean startAutomatically(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun != null)
|
||||
|
@@ -9,6 +9,7 @@ package net.i2p.i2ptunnel.web;
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -41,10 +42,10 @@ public class IndexBean {
|
||||
protected TunnelControllerGroup _group;
|
||||
private String _action;
|
||||
private int _tunnel;
|
||||
private long _prevNonce;
|
||||
private long _prevNonce2;
|
||||
private long _curNonce;
|
||||
private long _nextNonce;
|
||||
//private long _prevNonce;
|
||||
//private long _prevNonce2;
|
||||
private String _curNonce;
|
||||
//private long _nextNonce;
|
||||
|
||||
private String _type;
|
||||
private String _name;
|
||||
@@ -85,10 +86,14 @@ public class IndexBean {
|
||||
/** deprecated unimplemented, now using routerconsole realm */
|
||||
//public static final String PROP_TUNNEL_PASSPHRASE = "i2ptunnel.passphrase";
|
||||
public static final String PROP_TUNNEL_PASSPHRASE = "consolePassword";
|
||||
static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
|
||||
static final String PROP_NONCE_OLD = PROP_NONCE + '2';
|
||||
//static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
|
||||
//static final String PROP_NONCE_OLD = PROP_NONCE + '2';
|
||||
/** 3 wasn't enough for some browsers. They are reloading the page for some reason - maybe HEAD? @since 0.8.1 */
|
||||
private static final int MAX_NONCES = 5;
|
||||
/** store nonces in a static FIFO instead of in System Properties @since 0.8.1 */
|
||||
private static final List<String> _nonces = new ArrayList(MAX_NONCES + 1);
|
||||
|
||||
static final String CLIENT_NICKNAME = "shared clients";
|
||||
|
||||
public static final String PROP_THEME_NAME = "routerconsole.theme";
|
||||
public static final String DEFAULT_THEME = "light";
|
||||
public static final String PROP_CSS_DISABLED = "routerconsole.css.disabled";
|
||||
@@ -98,34 +103,39 @@ public class IndexBean {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(IndexBean.class);
|
||||
_group = TunnelControllerGroup.getInstance();
|
||||
_action = null;
|
||||
_tunnel = -1;
|
||||
_curNonce = -1;
|
||||
_prevNonce = -1;
|
||||
_prevNonce2 = -1;
|
||||
try {
|
||||
String nonce2 = System.getProperty(PROP_NONCE_OLD);
|
||||
if (nonce2 != null)
|
||||
_prevNonce2 = Long.parseLong(nonce2);
|
||||
String nonce = System.getProperty(PROP_NONCE);
|
||||
if (nonce != null) {
|
||||
_prevNonce = Long.parseLong(nonce);
|
||||
System.setProperty(PROP_NONCE_OLD, nonce);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {}
|
||||
_nextNonce = _context.random().nextLong();
|
||||
System.setProperty(PROP_NONCE, Long.toString(_nextNonce));
|
||||
_curNonce = "-1";
|
||||
addNonce();
|
||||
_booleanOptions = new ConcurrentHashSet(4);
|
||||
_otherOptions = new ConcurrentHashMap(4);
|
||||
}
|
||||
|
||||
public long getNextNonce() { return _nextNonce; }
|
||||
public static String getNextNonce() {
|
||||
synchronized (_nonces) {
|
||||
return _nonces.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNonce(String nonce) {
|
||||
if ( (nonce == null) || (nonce.trim().length() <= 0) ) return;
|
||||
try {
|
||||
_curNonce = Long.parseLong(nonce);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_curNonce = -1;
|
||||
_curNonce = nonce;
|
||||
}
|
||||
|
||||
/** add a random nonce to the head of the queue @since 0.8.1 */
|
||||
private void addNonce() {
|
||||
String nextNonce = Long.toString(_context.random().nextLong());
|
||||
synchronized (_nonces) {
|
||||
_nonces.add(0, nextNonce);
|
||||
int sz = _nonces.size();
|
||||
if (sz > MAX_NONCES)
|
||||
_nonces.remove(sz - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** do we know this nonce? @since 0.8.1 */
|
||||
private static boolean haveNonce(String nonce) {
|
||||
synchronized (_nonces) {
|
||||
return _nonces.contains(nonce);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +165,7 @@ public class IndexBean {
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) || ("Cancel".equals(_action)))
|
||||
return "";
|
||||
if ( (_prevNonce != _curNonce) && (_prevNonce2 != _curNonce) && (!validPassphrase()) )
|
||||
if ( (!haveNonce(_curNonce)) && (!validPassphrase()) )
|
||||
return "Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit.";
|
||||
if ("Stop all".equals(_action))
|
||||
return stopAll();
|
||||
|
@@ -196,7 +196,16 @@
|
||||
<a href="/susidns/addressbook.jsp?book=private&hostname=<%=editBean.getTunnelName(curTunnel)%>&destination=<%=editBean.getDestinationBase64(curTunnel)%>#add"><%=intl._("Add to local addressbook")%></a>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
|
||||
<% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
|
||||
%><div id="sigField" class="rowItem">
|
||||
<label for="signature">
|
||||
<%=intl._("Hostname Signature")%>
|
||||
</label>
|
||||
<input type="text" size="30" readonly="readonly" title="Use to prove that the website name is for this destination" value="<%=editBean.getNameSignature(curTunnel)%>" wrap="off" class="freetext" />
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="footer">
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user