SPARK-2099: TransferGuard case-insensitive blocking by file extension

This commit is contained in:
Sergey Ponomarev
2026-07-26 19:53:32 +03:00
parent ad4c86c036
commit 24d47fa510
8 changed files with 100 additions and 119 deletions

View File

@ -19,7 +19,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
@ -34,40 +35,32 @@ import org.jxmpp.jid.util.JidUtil;
* Bean whose properties are the various preference settings for file transfer.
*/
public class FileTransferSettings {
private List<String> extensions = new ArrayList<>();
private List<EntityBareJid> JIDs = new ArrayList<>();
private Set<String> extensions = Set.of();
private Set<EntityBareJid> JIDs = Set.of();
private int kb;
private boolean checkSize = false;
String cannedRejectionMessage;
private boolean checkSize;
private String cannedRejectionMessage;
private static final File BACKING_STORE = new File(Spark.getSparkUserHome(), "/transferguard.properties");
/**
* Returns a {@link List} of strings - one for each blocked file extension. Strings are in the form <tt>*.{extension}</tt>.
* Returns a {@link Set} of strings - one for each blocked file extension. Strings are in the form <tt>*.{extension}</tt>.
*/
public List<String> getBlockedExtensions(){
public Set<String> getBlockedExtensions(){
return extensions;
}
/**
* Sets the {@link List} of blocked file extensions.
* @param extensions the {@link List} of blocked file extensions.
*/
public void setBlockedExtensions(List<String> extensions){
public void setBlockedExtensions(Set<String> extensions){
this.extensions = extensions;
}
/**
* Returns a {@link List} of blocked JIDs. File transfers from users with those JIDs will be automaticlly rejected.
* Returns a {@link Set} of blocked JIDs. File transfers from users with those JIDs will be automatically rejected.
*/
public List<EntityBareJid> getBlockedJIDs() {
public Set<EntityBareJid> getBlockedJIDs() {
return JIDs;
}
/**
* Sets the {@link List} of blocked JIDs.
* @param JIDs the {@link List} of blocked JIDs.
*/
public void setBlockedJIDS(List<EntityBareJid> JIDs){
public void setBlockedJIDS(Set<EntityBareJid> JIDs){
this.JIDs = JIDs;
}
@ -79,10 +72,6 @@ public class FileTransferSettings {
return kb;
}
/**
* Sets the maximum file size in kilobytes for file transfers.
* @param kb the maximum file size in kilobytes for file transfers.
*/
public void setMaxFileSize(int kb){
this.kb = kb;
}
@ -94,11 +83,6 @@ public class FileTransferSettings {
return checkSize;
}
/**
* If set to true, files larger than the maximum file size as returned by {@link #getMaxFileSize}
* will not be accepted.
* @param checkSize true if size should be checked.
*/
public void setCheckFileSize(boolean checkSize){
this.checkSize = checkSize;
}
@ -111,11 +95,6 @@ public class FileTransferSettings {
return cannedRejectionMessage;
}
/**
* Sets the text of a canned message sent to requestors whose file transfers were automatically rejected. If set
* to null or an empty string, no message will be sent.
* @param cannedRejectionMessage the canned message text.
*/
public void setCannedRejectionMessage(String cannedRejectionMessage) {
this.cannedRejectionMessage = cannedRejectionMessage;
}
@ -138,9 +117,9 @@ public class FileTransferSettings {
String users = props.getProperty("jids");
if (users != null) {
List<String> jidStrings = convertSettingsStringToList(users);
Set<String> jidStrings = convertSettingsStringToList(users);
Set<EntityBareJid> jidSet = JidUtil.entityBareJidSetFrom(jidStrings);
this.JIDs = new ArrayList<>(jidSet);
this.JIDs = jidSet;
}
String ignore = props.getProperty("checkFileSize");
@ -170,6 +149,8 @@ public class FileTransferSettings {
props.setProperty("maxSize", Integer.toString(kb));
if (cannedRejectionMessage != null) {
props.setProperty("cannedResponse", cannedRejectionMessage);
} else {
props.remove("cannedResponse");
}
props.store(new FileOutputStream(BACKING_STORE), null);
} catch (IOException ioe) {
@ -178,33 +159,21 @@ public class FileTransferSettings {
}
/**
* Converts a list of strings to a single comma separated string
* @param settings the {@link List} of strings.
* Converts a list of strings to a single comma-separated string
*/
public static String convertSettingsListToString(List<? extends CharSequence> settings) {
StringBuilder buffer = new StringBuilder();
boolean first = true;
for (CharSequence cs : settings) {
if (!first) {
buffer.append(',');
} else {
first = false;
}
buffer.append(cs);
}
return buffer.toString();
public static String convertSettingsListToString(Collection<? extends CharSequence> settings) {
return String.join(",", settings);
}
/**
* Converts the supplied string to a {@link List} of strings. The input is split
* with the tokensL: ',' ':' '\n' '\t' '\r' and ' '.
* @param settings the string to convert.
* Converts the supplied string to a {@link List} of strings in lower case.
* The input is split with the tokens: ',' ':' '\n' '\t' '\r' and ' '.
*/
public static List<String> convertSettingsStringToList(String settings) {
List<String> list = new ArrayList<>();
public static Set<String> convertSettingsStringToList(String settings) {
HashSet<String> list = new HashSet<>();
StringTokenizer tokenizer = new StringTokenizer(settings, ",;\n\t\r ");
while (tokenizer.hasMoreTokens()) {
list.add(tokenizer.nextToken());
list.add(tokenizer.nextToken().toLowerCase());
}
return list;
}

View File

@ -26,8 +26,10 @@ import org.jivesoftware.spark.plugin.Plugin;
import org.jivesoftware.spark.preference.PreferenceManager;
import org.jivesoftware.spark.util.log.Log;
import static org.apache.commons.lang3.StringUtils.isBlank;
/**
* Spark plugin which allows configuration of allowed file sizes, types, and senders for file transfer.
* Spark plugin that allows configuration of allowed file sizes, types, and senders for file transfer.
* Transfer requests which don't meet the configured preferences are automatically rejected.
*/
public class FileTransferSettingsPlugin implements Plugin {
@ -46,7 +48,7 @@ public class FileTransferSettingsPlugin implements Plugin {
request.reject();
String responseMessage = settings.getCannedRejectionMessage();
if (responseMessage != null && !responseMessage.isEmpty()) {
if (!isBlank(responseMessage)) {
Message message = StanzaBuilder.buildMessage()
.to(request.getRequestor())
.setBody(responseMessage)
@ -97,8 +99,8 @@ public class FileTransferSettingsPlugin implements Plugin {
}
/**
* Strips the extension off the supplied filename and prepends an asterisk. For example 'bad.doc' would return
* '*.doc'.
* Strips the extension off the supplied filename, lowercase and prepends an asterisk.
* For example, 'bad.DOC' would return '*.doc'.
*
* @param filename to return the extension for.
* @return the extension.
@ -106,7 +108,7 @@ public class FileTransferSettingsPlugin implements Plugin {
private String getFileExtensionFromName(String filename) {
int dotIdx = filename.lastIndexOf(".");
if (dotIdx > 0 && dotIdx < (filename.length() - 1)) {
return "*" + filename.substring( dotIdx );
return "*" + filename.substring( dotIdx ).toLowerCase();
}
return null;
}

View File

@ -5,26 +5,28 @@ import java.util.ResourceBundle;
import org.jivesoftware.spark.util.log.Log;
import javax.swing.Icon;
import javax.swing.ImageIcon;
/**
* Use for Transferguard Ressource Internationalization.
* Use for TransferGuard Ressource Internationalization.
*
* @author tim.jentz
* @author Tim Jentz
*/
public class TGuardRes {
private static final PropertyResourceBundle prb = (PropertyResourceBundle) ResourceBundle.getBundle("i18n/transferguard_i18n");
private static ClassLoader cl = TGuardRes.class.getClassLoader();
static final Icon TRANSFERGUARD_ICON = new ImageIcon(cl.getResource("/images/transferguard/guard.png"));
private TGuardRes() {
}
public static String getString(String propertyName) {
try {
return prb.getString(propertyName);
} catch (Exception e) {
Log.error(e);
return propertyName;
}
static String getString(String propertyName) {
try {
return prb.getString(propertyName);
} catch (Exception e) {
Log.error(e);
return propertyName;
}
}
}

View File

@ -19,14 +19,26 @@ import org.jivesoftware.spark.component.VerticalFlowLayout;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.util.JidUtil;
import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import java.util.ArrayList;
import java.util.List;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.Set;
import static java.awt.GridBagConstraints.HORIZONTAL;
import static java.awt.GridBagConstraints.NONE;
import static java.awt.GridBagConstraints.WEST;
/**
* UI for the file transfer preferences. It displays all the various preference settings for editing.
*/
@ -46,7 +58,8 @@ public class TransferSettingsPanel extends JPanel {
/**
* Populates all the gui controls with values from the supplied {@link FileTransferSettings}.
* @param settings the {@link FileTransferSettings} to populate the gui from.
*
* @param settings the {@link FileTransferSettings} to populate the gui from.
*/
public void applySettings(FileTransferSettings settings) {
pnlTypes.setBlockedTypes(settings.getBlockedExtensions());
@ -58,6 +71,7 @@ public class TransferSettingsPanel extends JPanel {
/**
* Populates the supplied {@link FileTransferSettings} from the values in the gui controls.
*
* @param settings the {@link FileTransferSettings} to populate.
*/
public void storeSettings(FileTransferSettings settings) {
@ -69,51 +83,52 @@ public class TransferSettingsPanel extends JPanel {
}
private static class BlockedTypesPanel extends JPanel {
private final JTextArea txtBlockedTypes = new JTextArea(2, 0);
private final JTextArea txtBlockedTypes = new JTextArea(2, 0);
BlockedTypesPanel() {
txtBlockedTypes.setBorder(UIManager.getLookAndFeelDefaults().getBorder("TextField.border"));
txtBlockedTypes.setToolTipText(TGuardRes.getString("guard.settings.tooltips.blockedtypes"));
setLayout(new BorderLayout());
setBorder(BorderFactory.createCompoundBorder(new TitledBorder(TGuardRes.getString("guard.settings.title.extensions")),
new EmptyBorder(2,4,4,4)));
TitledBorder outsideBorder = new TitledBorder(TGuardRes.getString("guard.settings.title.extensions"));
setBorder(BorderFactory.createCompoundBorder(outsideBorder, new EmptyBorder(2, 4, 4, 4)));
add(txtBlockedTypes, BorderLayout.CENTER);
}
public void setBlockedTypes(List<String> types) {
public void setBlockedTypes(Set<String> types) {
txtBlockedTypes.setText(FileTransferSettings.convertSettingsListToString(types));
}
public List<String> getBlockedTypes() {
public Set<String> getBlockedTypes() {
return FileTransferSettings.convertSettingsStringToList(txtBlockedTypes.getText());
}
}
private static class BlockedPeoplePanel extends JPanel {
private final JTextArea txtBlockedPeople = new JTextArea(2, 0);
private final JTextArea txtBlockedPeople = new JTextArea(2, 0);
BlockedPeoplePanel() {
txtBlockedPeople.setBorder(UIManager.getLookAndFeelDefaults().getBorder("TextField.border"));
txtBlockedPeople.setToolTipText(TGuardRes.getString("guard.settings.tooltips.blockedperson"));
setLayout(new BorderLayout());
setBorder(BorderFactory.createCompoundBorder(new TitledBorder(TGuardRes.getString("guard.settings.title.person")),
new EmptyBorder(2,4,4,4)));
TitledBorder outsideBorder = new TitledBorder(TGuardRes.getString("guard.settings.title.person"));
setBorder(BorderFactory.createCompoundBorder(outsideBorder, new EmptyBorder(2, 4, 4, 4)));
add(txtBlockedPeople, BorderLayout.CENTER);
}
public void setBlockedPeople(List<EntityBareJid> people) {
public void setBlockedPeople(Set<EntityBareJid> people) {
txtBlockedPeople.setText(FileTransferSettings.convertSettingsListToString(people));
}
public List<EntityBareJid> getBlockedPeople() {
List<String> jidStrings = FileTransferSettings.convertSettingsStringToList(txtBlockedPeople.getText());
public Set<EntityBareJid> getBlockedPeople() {
Set<String> jidStrings = FileTransferSettings.convertSettingsStringToList(txtBlockedPeople.getText());
Set<EntityBareJid> jidSet = JidUtil.entityBareJidSetFrom(jidStrings);
return new ArrayList<>(jidSet);
return jidSet;
}
}
private static class FileSizePanel extends JPanel {
private final JSpinner spinMaxSize = new JSpinner();
private final JLabel spinMaxSizeLabel = new JLabel(TGuardRes.getString(("guard.settings.label.maxsize")));
private final JSpinner spinMaxSize = new JSpinner();
private final JCheckBox chkMaxEnabled = new JCheckBox(TGuardRes.getString("guard.settings.limitcheck"));
FileSizePanel() {
@ -122,19 +137,14 @@ public class TransferSettingsPanel extends JPanel {
add(chkMaxEnabled);
JPanel pnlSpinner = new JPanel(new GridBagLayout());
pnlSpinner.add(new JLabel(TGuardRes.getString(("guard.settings.label.maxsize"))),
new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,0,0,2), 0, 0));
pnlSpinner.add(spinMaxSize,
new GridBagConstraints(1, 0, 1, 1, 0.25, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0));
pnlSpinner.add(new JPanel(),
new GridBagConstraints(2, 0, 1, 1, 0.75, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0));
pnlSpinner.add(spinMaxSizeLabel, new GridBagConstraints(0, 0, 1, 1, 0, 0, WEST, NONE, new Insets(0, 0, 0, 2), 0, 0));
pnlSpinner.add(spinMaxSize, new GridBagConstraints(1, 0, 1, 1, 0.25, 0, WEST, HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
pnlSpinner.add(new JPanel(), new GridBagConstraints(2, 0, 1, 1, 0.75, 0, WEST, HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
add(pnlSpinner);
chkMaxEnabled.addActionListener( evnt -> {
if (spinMaxSize != null) {
spinMaxSize.setEnabled(chkMaxEnabled.isSelected());
}
} );
chkMaxEnabled.addActionListener(e -> {
spinMaxSize.setEnabled(chkMaxEnabled.isSelected());
});
}
public void setMaxFileSize(int kb) {
@ -155,14 +165,14 @@ public class TransferSettingsPanel extends JPanel {
}
private static class CannedResponsePanel extends JPanel {
private final JTextArea txtMessage = new JTextArea(2, 0);
private final JTextArea txtMessage = new JTextArea(2, 0);
CannedResponsePanel() {
txtMessage.setBorder(UIManager.getLookAndFeelDefaults().getBorder("TextField.border"));
txtMessage.setToolTipText(TGuardRes.getString(("guard.settings.tooltips.textarea")));
setLayout(new BorderLayout());
setBorder(BorderFactory.createCompoundBorder(new TitledBorder(TGuardRes.getString(("guard.settings.title.rejectresponse"))),
new EmptyBorder(2,4,4,4)));
TitledBorder outsideBorder = new TitledBorder(TGuardRes.getString(("guard.settings.title.rejectresponse")));
setBorder(BorderFactory.createCompoundBorder(outsideBorder, new EmptyBorder(2, 4, 4, 4)));
add(txtMessage, BorderLayout.CENTER);
}

View File

@ -18,11 +18,11 @@ package org.jivesoftware.spark.plugins.transfersettings;
import org.jivesoftware.spark.preference.Preference;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
/**
* Preference object for file transfer settings. Used by Spark to show the associated UI and commit changes to preference settings.
* Preference object for file transfer settings.
* Used by Spark to show the associated UI and commit changes to preference settings.
*/
public class TransferSettingsPreference implements Preference {
static final String NAMESPACE = "transferSettings";
@ -54,7 +54,7 @@ public class TransferSettingsPreference implements Preference {
@Override
public Icon getIcon() {
return new ImageIcon(getClass().getResource("/images/guard.png"));
return TGuardRes.TRANSFERGUARD_ICON;
}
@Override

View File

@ -43,24 +43,22 @@
<h1>
TransferGuard Spark plug Changelog
</h1>
<p><b>1.3</b> -- July 26, 2026</p>
<ul>
<li>SPARK-2099: TransferGuard case-insensitive blocking by file extension</li>
</ul>
<p><b>1.0.2</b> -- August 15th, 2013</p>
<ul>
<li>Updated polnish language file.</li>
<li>Updated Polish language file.</li>
</ul>
<p><b>1.0.1</b> -- September 19th, 2006</p>
<ul>
<li>Moving over to be Spark 2.0 compatible.</li>
<li>Moving over to be Spark 2.0 compatible.</li>
</ul>
<p><b>1.0.0</b> -- February 9th, 2006</p>
<ul>
<li>Initial release.</li>
<li>Initial release.</li>
</ul>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 547 B

After

Width:  |  Height:  |  Size: 547 B