Commit 7aaf1b0f authored by Dave Cridland's avatar Dave Cridland Committed by GitHub

Merge pull request #720 from guusdk/OF-512_filetransferproxy

OF-512: Allow 'reported' streamhost to be modified.
parents 92c97e50 3f76e421
......@@ -2705,9 +2705,12 @@ filetransferproxy.settings.label_disable=Disabled
filetransferproxy.settings.label_disable_info=This server will not act as a file transfer proxy.
filetransferproxy.settings.label_enable=Enabled
filetransferproxy.settings.label_enable_info=This server will act as a file transfer proxy on port:
filetransferproxy.settings.label_port=Port:
filetransferproxy.settings.label_hardcoded_address=Override reported address
filetransferproxy.settings.label_hardcoded_optionality=(leave empty if an override is not needed)
filetransferproxy.settings.valid.port=Please enter a valid port.
filetransferproxy.settings.confirm.updated=File transfer proxy settings updated succesfully.
filetransfer.error.disabled=File Transfer Has Been Disabled, Proxy Won't Function
filetransfer.error.disabled=File Transfer Has Been Disabled, Proxy Won't Function.
# File Transfer Proxy Stats
......
......@@ -63,8 +63,8 @@ import org.xmpp.packet.PacketError;
public class FileTransferProxy extends BasicModule
implements ServerItemsProvider, DiscoInfoProvider, DiscoItemsProvider,
RoutableChannelHandler {
private static final Logger Log = LoggerFactory.getLogger(FileTransferProxy.class);
private static final Logger Log = LoggerFactory.getLogger( FileTransferProxy.class);
/**
* The JiveProperty relating to whether or not the file treansfer proxy is enabled.
......@@ -77,6 +77,11 @@ public class FileTransferProxy extends BasicModule
*/
public static final String JIVEPROPERTY_PORT = "xmpp.proxy.port";
/**
* Name of the property that hardcodes the external IP that is being listened on.
*/
public static final String PROPERTY_EXTERNALIP = "xmpp.proxy.externalip";
/**
* Whether or not the file transfer proxy is enabled by default.
*/
......@@ -132,15 +137,28 @@ public class FileTransferProxy extends BasicModule
else if (FileTransferManager.NAMESPACE_BYTESTREAMS.equals(namespace)) {
if (packet.getType() == IQ.Type.get) {
IQ reply = IQ.createResultIQ(packet);
Element newChild = reply.setChildElement("query",
FileTransferManager.NAMESPACE_BYTESTREAMS);
for ( InetAddress address : getAddresses() )
Element newChild = reply.setChildElement("query", FileTransferManager.NAMESPACE_BYTESTREAMS);
final String externalIP = JiveGlobals.getProperty( PROPERTY_EXTERNALIP );
if ( externalIP != null && !externalIP.isEmpty() )
{
Element response = newChild.addElement( "streamhost" );
// OF-512: Override the automatic detection with a specific address (useful for NATs, proxies, etc)
final Element response = newChild.addElement( "streamhost" );
response.addAttribute( "jid", getServiceDomain() );
response.addAttribute( "host", address.getHostAddress() );
response.addAttribute( "host", externalIP );
response.addAttribute( "port", String.valueOf( connectionManager.getProxyPort() ) );
}
else
{
// Report all network addresses that we know that we're servicing.
for ( final InetAddress address : getAddresses() )
{
final Element response = newChild.addElement( "streamhost" );
response.addAttribute( "jid", getServiceDomain() );
response.addAttribute( "host", address.getHostAddress() );
response.addAttribute( "port", String.valueOf( connectionManager.getProxyPort() ) );
}
}
router.route(reply);
return true;
}
......@@ -179,71 +197,54 @@ public class FileTransferProxy extends BasicModule
routingTable = server.getRoutingTable();
router = server.getPacketRouter();
final String hardCodedProxyIP = JiveGlobals.getProperty( "xmpp.proxy.externalip" );
connectionManager = new ProxyConnectionManager(getFileTransferManager(server));
}
/**
* Returns the IP address(es) that the proxy connection manager is servicing.
*/
private Set<InetAddress> getAddresses()
{
final String interfaceName = JiveGlobals.getXMLProperty( "network.interface" );
if ( hardCodedProxyIP != null && !hardCodedProxyIP.trim().isEmpty() )
{
// First choice: a hardcoded IP address, if one exists.
try
{
bindInterface = InetAddress.getByName( hardCodedProxyIP.trim() );
}
catch ( UnknownHostException e )
{
Log.error( "Error binding to xmpp.proxy.externalip '{}'", interfaceName, e );
}
}
else if ( interfaceName != null && !interfaceName.trim().isEmpty() )
final Set<InetAddress> result = new HashSet<>();
// Let's see if we hardcoded a specific interface, then use its address.
if ( interfaceName != null && !interfaceName.trim().isEmpty() )
{
// No hardcoded IP? Let's see if we hardcoded a specific interface, then use its address.
try
{
bindInterface = InetAddress.getByName( interfaceName.trim() );
result.add( bindInterface );
return result;
}
catch ( UnknownHostException e )
{
Log.error( "Error binding to network.interface '{}'", interfaceName, e );
}
}
else
{
// If no configuration is available, use all available addresses.
bindInterface = null;
}
connectionManager = new ProxyConnectionManager(getFileTransferManager(server));
}
/**
* Returns the IP address(es) that are used.
*/
private Set<InetAddress> getAddresses()
{
final Set<InetAddress> result = new HashSet<>();
if ( bindInterface != null )
// When there's no specific address configured, return all available (non-loopback) addresses.
try
{
result.add( bindInterface );
}
else
{
// When there's no specific address configured, return all available addresses.
try
final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while ( networkInterfaces.hasMoreElements() )
{
final Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while ( networkInterfaces.hasMoreElements() )
final NetworkInterface networkInterface = networkInterfaces.nextElement();
if ( networkInterface.isLoopback() )
{
final NetworkInterface networkInterface = networkInterfaces.nextElement();
final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while ( inetAddresses.hasMoreElements() )
{
result.add( inetAddresses.nextElement() );
}
continue;
}
final Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while ( inetAddresses.hasMoreElements() )
{
result.add( inetAddresses.nextElement() );
}
}
catch ( SocketException e )
{
Log.error( "Error determining all addresses for this server", e );
}
}
catch ( SocketException e )
{
Log.error( "Error determining all addresses for this server", e );
}
return result;
}
......@@ -253,7 +254,7 @@ public class FileTransferProxy extends BasicModule
}
@Override
public void start() {
public void start() {
super.start();
if (isEnabled()) {
......@@ -273,7 +274,7 @@ public class FileTransferProxy extends BasicModule
}
@Override
public void stop() {
public void stop() {
super.stop();
XMPPServer.getInstance().getIQDiscoItemsHandler()
......@@ -283,7 +284,7 @@ public class FileTransferProxy extends BasicModule
}
@Override
public void destroy() {
public void destroy() {
super.destroy();
connectionManager.shutdown();
......@@ -291,7 +292,8 @@ public class FileTransferProxy extends BasicModule
public void enableFileTransferProxy(boolean isEnabled) {
JiveGlobals.setProperty(FileTransferProxy.JIVEPROPERTY_PROXY_ENABLED,
Boolean.toString(isEnabled));
Boolean.toString(isEnabled));
setEnabled( isEnabled );
}
private void setEnabled(boolean isEnabled) {
......@@ -358,9 +360,9 @@ public class FileTransferProxy extends BasicModule
}
final DiscoServerItem item = new DiscoServerItem(new JID(
getServiceDomain()), "Socks 5 Bytestreams Proxy", null, null, this,
this);
getServiceDomain()), "Socks 5 Bytestreams Proxy", null, null, this,
this);
return Collections.singleton(item).iterator();
}
......@@ -378,7 +380,7 @@ public class FileTransferProxy extends BasicModule
@Override
public Iterator<String> getFeatures(String name, String node, JID senderJID) {
return Arrays.asList(FileTransferManager.NAMESPACE_BYTESTREAMS,
"http://jabber.org/protocol/disco#info").iterator();
"http://jabber.org/protocol/disco#info").iterator();
}
@Override
......@@ -415,11 +417,21 @@ public class FileTransferProxy extends BasicModule
private class FileTransferPropertyListener implements PropertyEventListener {
@Override
public void propertySet(String property, Map params) {
public void propertySet(String property, Map params)
{
if ( isEnabled() )
{
// Restart when configuration changed.
if (JIVEPROPERTY_PORT.equalsIgnoreCase( property ))
{
setEnabled( false );
setEnabled( true );
}
}
if(JIVEPROPERTY_PROXY_ENABLED.equalsIgnoreCase(property)) {
Object value = params.get("value");
boolean isEnabled = (value != null ? Boolean.parseBoolean(value.toString()) :
DEFAULT_IS_PROXY_ENABLED);
boolean isEnabled = (value != null ? Boolean.parseBoolean(value.toString()) : DEFAULT_IS_PROXY_ENABLED);
setEnabled(isEnabled);
}
}
......@@ -429,6 +441,16 @@ public class FileTransferProxy extends BasicModule
if(JIVEPROPERTY_PROXY_ENABLED.equalsIgnoreCase(property)) {
setEnabled(DEFAULT_IS_PROXY_ENABLED);
}
if ( isEnabled() )
{
// Restart when configuration changed.
if (JIVEPROPERTY_PORT.equalsIgnoreCase( property ) )
{
setEnabled( false );
setEnabled( true );
}
}
}
@Override
......
......@@ -22,65 +22,89 @@
<%@ page import="org.jivesoftware.openfire.filetransfer.proxy.FileTransferProxy" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="org.jivesoftware.openfire.XMPPServer"%>
<%@ page import="org.jivesoftware.openfire.XMPPServer" %>
<%@ page import="org.jivesoftware.util.JiveGlobals" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager"/>
<% webManager.init(request, response, session, application, out); %>
<% webManager.init( request, response, session, application, out ); %>
<%
Map<String, String> errors = new HashMap<String, String>();
FileTransferProxy transferProxy = XMPPServer.getInstance().getFileTransferProxy();
boolean isUpdated = request.getParameter("update") != null;
boolean isProxyEnabled = ParamUtils.getBooleanParameter(request, "proxyEnabled");
int port = ParamUtils.getIntParameter(request, "port", 0);
Cookie csrfCookie = CookieUtils.getCookie(request, "csrf");
String csrfParam = ParamUtils.getParameter(request, "csrf");
boolean isUpdated = request.getParameter( "update" ) != null;
boolean isProxyEnabled = ParamUtils.getBooleanParameter( request, "proxyEnabled" );
String hardcodedAddress = ParamUtils.getParameter( request, "hardcodedAddress" );
int port = ParamUtils.getIntParameter( request, "port", 0 );
Cookie csrfCookie = CookieUtils.getCookie( request, "csrf" );
String csrfParam = ParamUtils.getParameter( request, "csrf" );
if (isUpdated) {
if (csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals(csrfParam)) {
if ( isUpdated )
{
if ( csrfCookie == null || csrfParam == null || !csrfCookie.getValue().equals( csrfParam ) )
{
isUpdated = false;
errors.put("csrf", "CSRF Failure!");
errors.put( "csrf", "CSRF Failure!" );
}
}
csrfParam = StringUtils.randomString(15);
CookieUtils.setCookie(request, response, "csrf", csrfParam, -1);
pageContext.setAttribute("csrf", csrfParam);
if (isUpdated) {
if (isProxyEnabled) {
if (port <= 0) {
errors.put("port", "");
}
csrfParam = StringUtils.randomString( 15 );
CookieUtils.setCookie( request, response, "csrf", csrfParam, -1 );
pageContext.setAttribute( "csrf", csrfParam );
if ( isUpdated )
{
if ( hardcodedAddress != null && !hardcodedAddress.matches( "^[A-Za-z0-9-\\.]+$" ) )
{
errors.put( "address", "" );
}
if ( port <= 0 )
{
errors.put( "port", "" );
}
if (errors.isEmpty()) {
if (isProxyEnabled) {
transferProxy.setProxyPort(port);
if ( errors.isEmpty() )
{
JiveGlobals.setProperty( "xmpp.proxy.externalip", hardcodedAddress );
if ( isProxyEnabled )
{
transferProxy.setProxyPort( port );
}
transferProxy.enableFileTransferProxy(isProxyEnabled);
transferProxy.enableFileTransferProxy( isProxyEnabled );
// Log the event
webManager.logEvent("edited file transfer proxy settings", "port = "+port+"\nenabled = "+isProxyEnabled);
webManager.logEvent( "edited file transfer proxy settings", "port = " + port + "\nhardcodedAddress = " + hardcodedAddress + "\nenabled = " + isProxyEnabled );
}
}
if (errors.isEmpty()) {
isProxyEnabled = transferProxy.isProxyEnabled();
if ( errors.isEmpty() )
{
port = transferProxy.getProxyPort();
}
else {
if (port == 0) {
else
{
isUpdated = false;
if ( port == 0 )
{
port = transferProxy.getProxyPort();
}
}
pageContext.setAttribute( "errors", errors );
pageContext.setAttribute( "isUpdated", isUpdated );
pageContext.setAttribute( "port", port );
pageContext.setAttribute( "hardcodedAddress", JiveGlobals.getProperty( "xmpp.proxy.externalip" ) );
pageContext.setAttribute( "fileTransferProxy", XMPPServer.getInstance().getFileTransferProxy() );
%>
<html>
<head>
<title><fmt:message key="filetransferproxy.settings.title"/></title>
<title><fmt:message key="filetransferproxy.settings.title"/></title>
</head>
<meta name="pageID" content="server-transfer-proxy"/>
<body>
......@@ -89,83 +113,75 @@
<fmt:message key="filetransferproxy.settings.info"/>
</p>
<% if (!errors.isEmpty()) { %>
<div class="jive-error">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td class="jive-icon"><img alt="error" src="images/error-16x16.gif" width="16" height="16"
border="0"/></td>
<td class="jive-icon-label">
<% if (errors.get("port") != null) { %>
<fmt:message key="filetransferproxy.settings.valid.port"/>
<% } %>
</td>
</tr>
</tbody>
</table>
</div>
<br>
<% }
else if (isUpdated) { %>
<div class="jive-success">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr><td class="jive-icon"><img alt="Success" src="images/success-16x16.gif" width="16" height="16"
border="0"></td>
<td class="jive-icon-label">
<fmt:message key="filetransferproxy.settings.confirm.updated"/>
</td></tr>
</tbody>
</table>
</div><br>
<% }
else { %>
<br>
<% } %>
<c:forEach var="err" items="${errors}" varStatus="varStatus">
<admin:infobox type="error">
<c:choose>
<c:when test="${err.key eq 'port'}">
<fmt:message key="filetransferproxy.settings.valid.port"/>
</c:when>
<c:otherwise>
<c:if test="${not empty err.value}">
<fmt:message key="admin.error"/>: <c:out value="${err.value}"/>
</c:if>
(<c:out value="${err.key}"/>)
</c:otherwise>
</c:choose>
</admin:infobox>
<c:if test="${not varStatus.last}">
<br/>
</c:if>
</c:forEach>
<c:if test="${isUpdated}">
<admin:infobox type="success">
<fmt:message key="filetransferproxy.settings.confirm.updated"/>
</admin:infobox>
</c:if>
<br/>
<!-- BEGIN 'Proxy Service' -->
<c:set var="title"><fmt:message key="filetransferproxy.settings.enabled.legend"/></c:set>
<form action="file-transfer-proxy.jsp" method="post">
<input type="hidden" name="csrf" value="${csrf}">
<div class="jive-contentBoxHeader">
<fmt:message key="filetransferproxy.settings.enabled.legend"/>
</div>
<div class="jive-contentBox">
<table cellpadding="3" cellspacing="0" border="0">
<tbody>
<tr valign="middle">
<td width="1%" nowrap>
<input type="radio" name="proxyEnabled" value="true" id="rb02"
<%= (isProxyEnabled ? "checked" : "") %> >
</td>
<td width="99%">
<label for="rb02">
<b><fmt:message key="filetransferproxy.settings.label_enable"/></b>
- <fmt:message key="filetransferproxy.settings.label_enable_info"/>
</label> <input type="text" size="5" maxlength="10" name="port"
value="<%= port %>" >
</td>
</tr>
<tr valign="middle">
<td width="1%" nowrap>
<input type="radio" name="proxyEnabled" value="false" id="rb01"
<%= (!isProxyEnabled ? "checked" : "") %> >
</td>
<td width="99%">
<label for="rb01">
<b><fmt:message key="filetransferproxy.settings.label_disable"/></b>
- <fmt:message key="filetransferproxy.settings.label_disable_info"/>
</label>
</td>
</tr>
</tbody>
</table>
</div>
<admin:contentBox title="${title}">
<table cellpadding="3" cellspacing="0" border="0">
<tbody>
<tr valign="top">
<td width="1%" nowrap>
<input type="radio" name="proxyEnabled" value="true" id="rb02" ${fileTransferProxy.proxyEnabled ? 'checked' : ''}>
</td>
<td width="99%">
<label for="rb02"><b><fmt:message key="filetransferproxy.settings.label_enable"/></b>- <fmt:message key="filetransferproxy.settings.label_enable_info"/></label>
<table border="0">
<tr>
<td><label for="port"><fmt:message key="filetransferproxy.settings.label_port"/></label></td>
<td><input type="text" size="5" maxlength="10" id="port" name="port" value="${port}"></td>
</tr>
<tr>
<td><label for="hardcodedAddress"><fmt:message key="filetransferproxy.settings.label_hardcoded_address"/></label></td>
<td>
<input type="text" size="40" maxlength="255" id="hardcodedAddress" name="hardcodedAddress" value="${hardcodedAddress}"> <fmt:message key="filetransferproxy.settings.label_hardcoded_optionality"/>
</td>
</tr>
</table>
</td>
</tr>
<tr valign="middle">
<td width="1%" nowrap>
<input type="radio" name="proxyEnabled" value="false" id="rb01" ${fileTransferProxy.proxyEnabled ? '' : 'checked'}>
</td>
<td width="99%">
<label for="rb01"><b><fmt:message key="filetransferproxy.settings.label_disable"/></b> - <fmt:message key="filetransferproxy.settings.label_disable_info"/></label>
</td>
</tr>
</tbody>
</table>
</admin:contentBox>
<input type="submit" name="update" value="<fmt:message key="global.save_settings" />">
</form>
<!-- END 'Proxy Service' -->
</body>
</html>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment