Commit 9f3f5ea7 authored by Alex Wenckus's avatar Alex Wenckus Committed by alex

1) Added ability to enable/disable file transfer. JM-675

2) Minor issue where socket wasn't being cleaned up correctly when the proxy was disabled. JM-710
3) Added proxy transfer statistic
4) File transfer cache is no longer public.

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3959 b35dd754-fafc-0310-a699-88a17e54d16e
parent 88b3d5e2
......@@ -1484,6 +1484,15 @@ filetransferproxy.settings.label_enable=Povolena
filetransferproxy.settings.label_enable_info=Tento server bude fungovat jako prost\u0159edn\u00edk p\u0159enosu soubor\u016f na portu:
filetransferproxy.settings.valid.port=Pros\u00edm zadejte platn\u00fd port.
filetransferproxy.settings.confirm.updated=Nastaven\u00ed prost\u0159edn\u00edka p\u0159enosu soubor\u016f \u00fasp\u011b\u0161n\u011b zm\u011bn\u011bno.
filetransfer.error.disabled=File Transfer Has Been Disabled, Proxy Won't Function
# File Transfer Proxy Stats
stat.filetransferproxy.transfered.name = Proxy Transfer Rate
stat.filetransferproxy.transfered.desc = The amount of data in kilobytes being transfered through the file transfer\
proxy.
stat.filetransferproxy.transfered.units = Kb/s
# System Cache page
system.cache.title=P\u0159ehled ke\u0161e
......
......@@ -1482,6 +1482,14 @@ filetransferproxy.settings.label_enable=Enabled
filetransferproxy.settings.label_enable_info=This server will act as a file transfer proxy on port:
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
# File Transfer Proxy Stats
stat.filetransferproxy.transfered.name = Proxy Transfer Rate
stat.filetransferproxy.transfered.desc = The amount of data in kilobytes being transfered through the file transfer\
proxy.
stat.filetransferproxy.transfered.units = Kb/s
# System Cache page
system.cache.title=Cache Summary
......
......@@ -178,6 +178,10 @@
## Added key: 'tab.tab-plugins.descr'
## Added key: 'sidebar.sidebar-plugin-admin'
## Added key: 'sidebar.plugin-settings'
## Added key: 'filetransfer.error.disabled'
## Added key: 'stat.filetransferproxy.transfered.name
## Added key: 'stat.filetransferproxy.transfered.desc
## Added key: 'stat.filetransferproxy.transfered.units
# Wildfire
......@@ -1674,6 +1678,14 @@ filetransferproxy.settings.label_enable=Enabled
filetransferproxy.settings.label_enable_info=This server will act as a file transfer proxy on port:
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
# File Transfer Proxy Stats
stat.filetransferproxy.transfered.name = Proxy Transfer Rate
stat.filetransferproxy.transfered.desc = The amount of data in kilobytes being transfered through the file transfer\
proxy.
stat.filetransferproxy.transfered.units = Kb/s
# System Cache page
system.cache.title=Cache Summary
......
......@@ -1356,6 +1356,14 @@ filetransferproxy.settings.label_enable=Activado
filetransferproxy.settings.label_enable_info=Este servidor funcionar\u00e1 como proxy de transferencia de archivos en el puerto:
filetransferproxy.settings.valid.port=Ingrese un puerto v\u00e1lido.
filetransferproxy.settings.confirm.updated=Configuraci\u00f3n actualizada con \u00e9xito.
filetransfer.error.disabled=File Transfer Has Been Disabled, Proxy Won't Function
# File Transfer Proxy Stats
stat.filetransferproxy.transfered.name = Proxy Transfer Rate
stat.filetransferproxy.transfered.desc = The amount of data in kilobytes being transfered through the file transfer\
proxy.
stat.filetransferproxy.transfered.units = Kb/s
# System Cache page
......@@ -1447,4 +1455,4 @@ pubsub.command.pending-subscriptions.error.idrequired=ID del nodo no encontrado.
pubsub.command.pending-subscriptions.error.manyIDs=M\u00e1s de un nodo fue seleccionado.
pubsub.command.pending-subscriptions.success=Las Suscripciones Pendientes han sido enviadas.
pubsub.command.pending-subscriptions.error.forbidden=Permiso denegado.
pubsub.command.pending-subscriptions.error.badid=ID de nodo no v\u00e1lido fue seleccionado.
\ No newline at end of file
pubsub.command.pending-subscriptions.error.badid=ID de nodo no v\u00e1lido fue seleccionado.
\ No newline at end of file
......@@ -1597,6 +1597,14 @@ filetransferproxy.settings.label_enable=Activ\u00e9
filetransferproxy.settings.label_enable_info=Ce serveur sera un proxy pour transfert de fichiers sur le port :
filetransferproxy.settings.valid.port=Veuillez saisir un port valide.
filetransferproxy.settings.confirm.updated=Les param\u00e8tres de proxy de transfert de fichiers ont \u00e9t\u00e9 mis \u00e0 jour avec succ\u00e8s.=======
filetransfer.error.disabled=File Transfer Has Been Disabled, Proxy Won't Function
# File Transfer Proxy Stats
stat.filetransferproxy.transfered.name = Proxy Transfer Rate
stat.filetransferproxy.transfered.desc = The amount of data in kilobytes being transfered through the file transfer\
proxy.
stat.filetransferproxy.transfered.units = Kb/s
# System Cache page
system.cache.title=Cache
......
......@@ -1485,6 +1485,14 @@ filetransferproxy.settings.label_enable=Enabled
filetransferproxy.settings.label_enable_info=This server will act as a file transfer proxy on port:
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
# File Transfer Proxy Stats
stat.filetransferproxy.transfered.name = Proxy Transfer Rate
stat.filetransferproxy.transfered.desc = The amount of data in kilobytes being transfered through the file transfer\
proxy.
stat.filetransferproxy.transfered.units = Kb/s
# System Cache page
system.cache.title=Cache Summary
......@@ -1574,4 +1582,4 @@ pubsub.command.pending-subscriptions.error.idrequired=ID of node is missing.
pubsub.command.pending-subscriptions.error.manyIDs=More than one nodeID was selected.
pubsub.command.pending-subscriptions.success=Pending subscriptions have been sent.
pubsub.command.pending-subscriptions.error.forbidden=Permission denied.
pubsub.command.pending-subscriptions.error.badid=Invalid node ID was selected.
\ No newline at end of file
pubsub.command.pending-subscriptions.error.badid=Invalid node ID was selected.
......@@ -1459,6 +1459,14 @@ filetransferproxy.settings.label_enable=Aktywny
filetransferproxy.settings.label_enable_info=Ten serwer b\u0119dzie pracowa\u0142 w trybie proxy na porcie:
filetransferproxy.settings.valid.port=Prosz\u0119 poda\u0107 poprawny numer portu.
filetransferproxy.settings.confirm.updated=Zaktualizowano ustawienia proxy.
filetransfer.error.disabled=File Transfer Has Been Disabled, Proxy Won't Function
# File Transfer Proxy Stats
stat.filetransferproxy.transfered.name = Proxy Transfer Rate
stat.filetransferproxy.transfered.desc = The amount of data in kilobytes being transfered through the file transfer\
proxy.
stat.filetransferproxy.transfered.units = Kb/s
# System Cache page
system.cache.title=W\u0142a\u015bciwo\u015bci cache
......
......@@ -1485,6 +1485,14 @@ filetransferproxy.settings.label_enable=Ativado
filetransferproxy.settings.label_enable_info=Este servidor atuar\u00e1 como um proxy de transfer\u00eancia de arquivos na porta:
filetransferproxy.settings.valid.port=Favor informar uma porta v\u00e1lida.
filetransferproxy.settings.confirm.updated=Configura\u00e7\u00f5es do proxy de transfer\u00eancia de arquivos atualizadas com sucesso.
filetransfer.error.disabled=File Transfer Has Been Disabled, Proxy Won't Function
# File Transfer Proxy Stats
stat.filetransferproxy.transfered.name = Proxy Transfer Rate
stat.filetransferproxy.transfered.desc = The amount of data in kilobytes being transfered through the file transfer\
proxy.
stat.filetransferproxy.transfered.units = Kb/s
# System Cache page
system.cache.title=Listagem de Cache
......@@ -1575,4 +1583,4 @@ pubsub.command.pending-subscriptions.error.idrequired=A ID do n\u00f3 est\u00e1
pubsub.command.pending-subscriptions.error.manyIDs=Mais de uma ID de n\u00f3 foi selecionada.
pubsub.command.pending-subscriptions.success=Subscri\u00e7\u00f5es pendentes foram enviadas.
pubsub.command.pending-subscriptions.error.forbidden=Permiss\u00e3o negada.
pubsub.command.pending-subscriptions.error.badid=Foi selecionada uma ID de n\u00f3 inv\u00e1lida.
pubsub.command.pending-subscriptions.error.badid=Foi selecionada uma ID de n\u00f3 inv\u00e1lida.
\ No newline at end of file
......@@ -1357,6 +1357,14 @@ filetransferproxy.settings.label_enable=\u4f7f\u7528
filetransferproxy.settings.label_enable_info=\u6b64\u670d\u52a1\u5668\u5728\u4e00\u4e2a\u6307\u5b9a\u7aef\u53e3\u505a\u4e3a\u4e00\u4e2a\u6587\u4ef6\u4ee3\u7406\u670d\u52a1\u5668\u4f7f\u7528:
filetransferproxy.settings.valid.port=\u8bf7\u8f93\u5165\u4e00\u4e2a\u6709\u6548\u7684\u7aef\u53e3\u3002
filetransferproxy.settings.confirm.updated=\u6587\u4ef6\u4f20\u9001\u4ee3\u7406\u8bbe\u7f6e\u66f4\u65b0\u6210\u529f\u3002
filetransfer.error.disabled=File Transfer Has Been Disabled, Proxy Won't Function
# File Transfer Proxy Stats
stat.filetransferproxy.transfered.name = Proxy Transfer Rate
stat.filetransferproxy.transfered.desc = The amount of data in kilobytes being transfered through the file transfer\
proxy.
stat.filetransferproxy.transfered.units = Kb/s
# System Cache page
system.cache.title=\u7f13\u5b58\u6458\u8981
......@@ -1441,3 +1449,4 @@ pubsub.command.pending-subscriptions.error.manyIDs=\u88ab\u9009\u7684\u8282\u70b
pubsub.command.pending-subscriptions.success=\u672a\u786e\u5b9a\u7684\u8ba2\u9605\u88ab\u4f20\u9001\u3002
pubsub.command.pending-subscriptions.error.forbidden=\u8bf7\u6c42\u88ab\u62d2\u7edd\u3002
pubsub.command.pending-subscriptions.error.badid=\u65e0\u6548\u7684\u8282\u70b9\u53f7\u88ab\u9009\u62e9\u3002
......@@ -10,8 +10,8 @@
*/
package org.jivesoftware.wildfire.filetransfer;
import org.jivesoftware.util.CacheManager;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Cache;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.dom4j.Element;
......@@ -28,17 +28,18 @@ public abstract class AbstractFileTransferManager implements FileTransferManager
private final Map<String, FileTransfer> fileTransferMap;
/**
* True if a proxy transfer is required to have a matching file transfer.
*/
private boolean isMatchProxyTransfer;
/**
* Default constructor creates the cache.
*/
public AbstractFileTransferManager() {
fileTransferMap = CacheManager.initializeCache(CACHE_NAME, "fileTransfer", 128 * 1024, 1000 * 60 * 10);
isMatchProxyTransfer = JiveGlobals.getBooleanProperty("xmpp.proxy.transfer.required", true);
fileTransferMap = createCache(CACHE_NAME, "fileTransfer", 128 * 1024, 1000 * 60 * 10);
}
private Cache<String, FileTransfer> createCache(String name, String propertiesName, int size, long expirationTime) {
size = JiveGlobals.getIntProperty("cache." + propertiesName + ".size", size);
expirationTime = (long) JiveGlobals.getIntProperty(
"cache." + propertiesName + ".expirationTime", (int) expirationTime);
return new Cache<String, FileTransfer>(name, size, expirationTime);
}
/**
......@@ -47,7 +48,7 @@ public abstract class AbstractFileTransferManager implements FileTransferManager
* @return Returns true if the proxy transfer should be matched to an existing file transfer in the system.
*/
public boolean isMatchProxyTransfer() {
return isMatchProxyTransfer;
return JiveGlobals.getBooleanProperty("xmpp.proxy.transfer.required", true);
}
public void cacheFileTransfer(String key, FileTransfer transfer) {
......
......@@ -13,12 +13,14 @@ package org.jivesoftware.wildfire.filetransfer;
import org.jivesoftware.util.Cacheable;
import org.jivesoftware.util.CacheSizes;
import java.io.Serializable;
/**
* Contains all of the meta information associated with a file transfer.
*
* @author Alexander Wenckus
*/
public class FileTransfer implements Cacheable {
public class FileTransfer implements Cacheable, Serializable {
private String sessionID;
private String initiator;
......
......@@ -13,6 +13,9 @@ package org.jivesoftware.wildfire.filetransfer;
import org.jivesoftware.util.*;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.filetransfer.spi.DefaultProxyTransfer;
import org.jivesoftware.wildfire.stats.Statistic;
import org.jivesoftware.wildfire.stats.StatisticsManager;
import org.jivesoftware.wildfire.stats.i18nStatistic;
import org.xmpp.packet.JID;
import java.io.*;
......@@ -33,6 +36,8 @@ import java.util.concurrent.Future;
*/
public class ProxyConnectionManager {
private static final String proxyTransferRate = "proxyTransferRate";
private Map<String, ProxyTransfer> connectionMap;
private final Object connectionLock = new Object();
......@@ -41,23 +46,26 @@ public class ProxyConnectionManager {
private Future<?> socketProcess;
private ServerSocket serverSocket;
private int proxyPort;
private FileTransferManager transferManager;
private String className;
static long amountTransfered = 0;
private ProxyTracker proxyTracker;
@SuppressWarnings({"unchecked"})
public ProxyConnectionManager(FileTransferManager manager) {
String cacheName = "File Transfer";
CacheManager.initializeCache(cacheName, "fileproxytransfer", -1, 1000 * 60 * 10);
connectionMap = CacheManager.getCache(cacheName);
connectionMap = CacheManager.initializeCache(cacheName, "fileproxytransfer", -1, 1000 * 60 * 10);
className = JiveGlobals.getProperty("provider.transfer.proxy",
"org.jivesoftware.wildfire.filetransfer.spi.DefaultProxyTransfer");
transferManager = manager;
this.proxyTracker = new ProxyTracker();
}
/*
......@@ -65,19 +73,13 @@ public class ProxyConnectionManager {
* This is the main loop of the manager which will run until the process is canceled.
*/
synchronized void processConnections(final int port) {
if (socketProcess != null) {
if (port != proxyPort) {
socketProcess.cancel(true);
socketProcess = null;
}
else {
return;
}
if (port == proxyPort) {
return;
}
reset(port);
socketProcess = executor.submit(new Runnable() {
public void run() {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(port);
}
......@@ -115,6 +117,7 @@ public class ProxyConnectionManager {
}
});
proxyPort = port;
StatisticsManager.getInstance().addStatistic(proxyTransferRate, proxyTracker);
}
public int getProxyPort() {
......@@ -197,9 +200,9 @@ public class ProxyConnectionManager {
return provider;
}
@SuppressWarnings({"ResultOfMethodCallIgnored"})
private static String processIncomingSocks5Message(InputStream in)
throws IOException
{
throws IOException {
// read the version and command
byte[] cmd = new byte[5];
int read = in.read(cmd, 0, 5);
......@@ -215,8 +218,8 @@ public class ProxyConnectionManager {
}
String digest = new String(addr);
read = in.read();
read = in.read();
in.read();
in.read();
return digest;
}
......@@ -241,6 +244,7 @@ public class ProxyConnectionManager {
synchronized void shutdown() {
disable();
executor.shutdown();
StatisticsManager.getInstance().removeStatistic(proxyTransferRate);
}
/**
......@@ -296,8 +300,7 @@ public class ProxyConnectionManager {
* @return SHA-1 hash of the three parameters
*/
public static String createDigest(final String sessionID, final JID initiator,
final JID target)
{
final JID target) {
return StringUtils.hash(sessionID + initiator.getNode()
+ "@" + initiator.getDomain() + "/"
+ initiator.getResource()
......@@ -307,13 +310,40 @@ public class ProxyConnectionManager {
}
public boolean isRunning() {
return socketProcess != null;
return socketProcess != null && !socketProcess.isDone();
}
public void disable() {
reset(-1);
}
private void reset(int port) {
if (socketProcess != null) {
socketProcess.cancel(true);
socketProcess = null;
if (port != proxyPort) {
socketProcess.cancel(true);
socketProcess = null;
}
else {
return;
}
}
if (serverSocket != null) {
try {
serverSocket.close();
}
catch (IOException e) {
Log.warn("Error closing proxy listening socket", e);
}
}
}
private static class ProxyTracker extends i18nStatistic {
public ProxyTracker() {
super("filetransferproxy.transfered", Statistic.Type.rate);
}
public double sample() {
return (ProxyOutputStream.amountTransfered.getAndSet(0) / 1000);
}
}
}
}
\ No newline at end of file
/**
* $RCSfile $
* $Revision $
* $Date $
*
* Copyright (C) 1999-2005 Jive Software. All rights reserved.
* This software is the proprietary information of Jive Software. Use is subject to license terms.
*/
package org.jivesoftware.wildfire.filetransfer;
import java.io.OutputStream;
import java.io.IOException;
import java.io.DataOutputStream;
import java.util.concurrent.atomic.AtomicLong;
/**
* An output stream which tracks the amount of bytes transfered by proxy sockets.
*/
public class ProxyOutputStream extends DataOutputStream {
static AtomicLong amountTransfered = new AtomicLong(0);
public ProxyOutputStream(OutputStream out) {
super(out);
}
public synchronized void write(byte b[], int off, int len) throws IOException {
super.write(b, off, len);
amountTransfered.addAndGet(len);
}
}
......@@ -17,7 +17,9 @@ import org.jivesoftware.wildfire.filetransfer.FileTransfer;
import org.jivesoftware.wildfire.filetransfer.ProxyConnectionManager;
/**
*
* The default implementation of the file transfer manager. The only acceptance criteria for a proxy
* transfer is employed in the <i>ProxyConnectionManager</i>, it checks that the file transfers stored
* hre
*/
public class DefaultFileTransferManager extends AbstractFileTransferManager {
public boolean acceptIncomingFileTransferRequest(String packetID, JID from, JID to, Element siElement) {
......
......@@ -12,20 +12,19 @@ package org.jivesoftware.wildfire.filetransfer.spi;
import org.jivesoftware.util.CacheSizes;
import org.jivesoftware.wildfire.filetransfer.ProxyTransfer;
import org.jivesoftware.wildfire.filetransfer.ProxyOutputStream;
import java.net.Socket;
import java.util.concurrent.Future;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.DataOutputStream;
/**
* Tracks the different connections related to a file transfer. There are two connections, the
* initiator and the target and when both connections are completed the transfer can begin.
*/
public class DefaultProxyTransfer implements ProxyTransfer {
private static long amountTransfered;
private String initiator;
......@@ -158,14 +157,4 @@ public class DefaultProxyTransfer implements ProxyTransfer {
return size;
}
static class ProxyOutputStream extends DataOutputStream {
public ProxyOutputStream(OutputStream out) {
super(out);
}
public synchronized void write(byte b[], int off, int len) throws IOException {
super.write(b, off, len);
amountTransfered += len;
}
}
}
......@@ -8,10 +8,10 @@
- a copy of which is included in this distribution.
--%>
<%@ page import="org.jivesoftware.util.ParamUtils" %>
<%@ page import="org.jivesoftware.wildfire.XMPPServer" %>
<%@ page import="org.jivesoftware.wildfire.filetransfer.FileTransferProxy" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="java.util.Map" %>
<%@ page import="org.jivesoftware.wildfire.XMPPServer"%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt_rt" prefix="fmt" %>
......@@ -20,9 +20,11 @@
<% webManager.init(request, response, session, application, out); %>
<%
Map errors = new HashMap();
Map<String, String> errors = new HashMap<String, String>();
FileTransferProxy transferProxy = XMPPServer.getInstance().getFileTransferProxy();
boolean isFileTransferEnabled = transferProxy.isFileTransferEnabled();
boolean isUpdated = request.getParameter("update") != null;
boolean isProxyEnabled = ParamUtils.getBooleanParameter(request, "proxyEnabled");
int port = ParamUtils.getIntParameter(request, "port", 0);
......@@ -38,12 +40,12 @@
if (isProxyEnabled) {
transferProxy.setProxyPort(port);
}
transferProxy.setEnabled(isProxyEnabled);
transferProxy.enableFileTransferProxy(isProxyEnabled);
}
}
if (errors.isEmpty()) {
isProxyEnabled = transferProxy.isEnabled();
isProxyEnabled = transferProxy.isProxyEnabled();
port = transferProxy.getProxyPort();
}
else {
......@@ -60,17 +62,18 @@
<p>
<fmt:message key="filetransferproxy.settings.info"/>
</p>
<% if (!errors.isEmpty()) { %>
<% if (!isFileTransferEnabled || !errors.isEmpty()) { %>
<div class="jive-error">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td class="jive-icon"><img src="images/error-16x16.gif" width="16" height="16"
<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) { %>
<% if(!isFileTransferEnabled) { %>
<fmt:message key="filetransfer.error.disabled"/>
<% } else if (errors.get("port") != null) { %>
<fmt:message key="filetransferproxy.settings.valid.port"/>
<% } %>
</td>
......@@ -85,7 +88,7 @@ else if (isUpdated) { %>
<div class="jive-success">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr><td class="jive-icon"><img src="images/success-16x16.gif" width="16" height="16"
<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"/>
......@@ -110,20 +113,20 @@ else { %>
<tr valign="middle">
<td width="1%" nowrap>
<input type="radio" name="proxyEnabled" value="true" id="rb02"
<%= (isProxyEnabled ? "checked" : "") %>>
<%= (isProxyEnabled ? "checked" : "") %> <%=!isFileTransferEnabled ? "disabled" : ""%>>
</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 %>">
value="<%= port %>" <%=!isFileTransferEnabled ? "disabled" : ""%>>
</td>
</tr>
<tr valign="middle">
<td width="1%" nowrap>
<input type="radio" name="proxyEnabled" value="false" id="rb01"
<%= (!isProxyEnabled ? "checked" : "") %>>
<%= (!isProxyEnabled ? "checked" : "") %> <%=!isFileTransferEnabled ? "disabled" : ""%>>
</td>
<td width="99%">
<label for="rb01">
......@@ -139,7 +142,8 @@ else { %>
</fieldset>
<br>
<input type="submit" name="update" value="<fmt:message key="global.save_settings" />">
<input type="submit" name="update" value="<fmt:message key="global.save_settings" />"
<%=!isFileTransferEnabled ? "disabled" : ""%>>
</form>
</body>
......
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