Commit 461670da authored by conor's avatar conor

added new features to content filter, see changelog.html for details

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@3262 b35dd754-fafc-0310-a699-88a17e54d16e
parent 8e88fb87
...@@ -44,6 +44,19 @@ ...@@ -44,6 +44,19 @@
ContentFilter Plugin Changelog ContentFilter Plugin Changelog
</h1> </h1>
<p><b>1.2</b> -- Jan, 2006</p>
<ul>
<li>Added support to notify administrator by email on content match notification.</li>
<li>Added support to include original packet when sending violation notifications.</li>
<li>Added support to filter users presence status.</li>
<li>Increased size of patterns input area to allow more patterns.</li>
<li>Fixed masking function.</li>
<li>Added debug logging to help users identify problems.</li>
<li>Updated readme.html</li>
</ul>
<p><b>1.0.1</b> -- December 15, 2005</p> <p><b>1.0.1</b> -- December 15, 2005</p>
<ul> <ul>
<li>Now requires Wildfire 2.4.0</li> <li>Now requires Wildfire 2.4.0</li>
......
...@@ -53,7 +53,7 @@ alternative content. ...@@ -53,7 +53,7 @@ alternative content.
<h2>Installation</h2> <h2>Installation</h2>
<p> <p>
Copy the contentfilter.jar into the plugins directory of your Wildfire Copy the contentfilter.jar into the plugins directory of your Jive Messenger
installation. The plugin will then be automatically deployed. To upgrade to a installation. The plugin will then be automatically deployed. To upgrade to a
new version, copy the new contentfilter.jar file over the existing file. new version, copy the new contentfilter.jar file over the existing file.
</p> </p>
...@@ -62,7 +62,7 @@ new version, copy the new contentfilter.jar file over the existing file. ...@@ -62,7 +62,7 @@ new version, copy the new contentfilter.jar file over the existing file.
<p> <p>
By default, after the plugin has been deployed all of its features are disabled. By default, after the plugin has been deployed all of its features are disabled.
This plugin is configured via the "Content Filter" sidebar item located under the This plugin is configured via the "Content Filter" sidebar item located under the
"System" tab in the Wildfire Admin Console. "System" tab in the Jive Messenger Admin Console.
</p> </p>
<p> <p>
...@@ -73,16 +73,28 @@ can be enhanced with more complex regular expressions as required e.g.: ...@@ -73,16 +73,28 @@ can be enhanced with more complex regular expressions as required e.g.:
<ul> <ul>
<li>for a complete word match, add boundary checks with \b e.g. \bfox\b will match against the word "fox" and nothing else. <li>for a complete word match, add boundary checks with \b e.g. \bfox\b will match against the word "fox" and nothing else.
<li>for case insensitive matchs add (?i) e.g. (?i)\bfox\b will match against "fox", "Fox", "foX" etc. <li>for case insensitive matchs add (?i) e.g. (?i)\bfox\b will match against "fox", "Fox", "foX" etc.
<li>it is also possible to group related patterns patterns e.g. fox|dog, this can be used to reduce the number of individual patterns to test for.
</ul> </ul>
</p>
<p>
If you choose to filter your users presence status and there is a content match then:
<ul>
<li>if you are masking content, other users will see a masked status.
<li>if you are rejecting content, other users not see the status change, how it affects the user with the invalid status is client dependant.
</ul>
</p>
<p>
Want to know more about regular expressions in Java? This official <a href="http://java.sun.com/docs/books/tutorial/extra/regex/">tutorial</a> Want to know more about regular expressions in Java? This official <a href="http://java.sun.com/docs/books/tutorial/extra/regex/">tutorial</a>
is useful. is useful.
</p> </p>
<p>
The default mask is "***", you can change it to anything you like including smilies!
</p>
<h2>Using the Plugin</h2> <h2>Using the Plugin</h2>
<p> <p>
After the plugin has been configured, nothing else needs to be done to use it. After the plugin has been configured, nothing else needs to be done to use it.
TODO - update
</p> </p>
</body> </body>
......
/** /**
* $RCSfile$ * $RCSfile$
* $Revision: 1594 $ * $Revision: 1594 $
* $Date: 2005-07-04 14:08:42 -0300 (Mon, 04 Jul 2005) $ * $Date: 2005-07-04 18:08:42 +0100 (Mon, 04 Jul 2005) $
* *
* Copyright (C) 2004 Jive Software. All rights reserved. * Copyright (C) 2004 Jive Software. All rights reserved.
* *
* This software is published under the terms of the GNU Public License (GPL), * This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution. * a copy of which is included in this distribution.
*/ */
package org.jivesoftware.wildfire.plugin; package org.jivesoftware.wildfire.plugin;
import org.xmpp.packet.Message; import java.util.ArrayList;
import java.util.Collection;
import java.util.ArrayList; import java.util.Iterator;
import java.util.Collection; import java.util.regex.Matcher;
import java.util.regex.Matcher; import java.util.regex.Pattern;
import java.util.regex.Pattern;
import org.dom4j.Element;
/** import org.xmpp.packet.Message;
* Filters message content using regular expressions. If a content mask is import org.xmpp.packet.Packet;
* provided message content will be altered.
* /**
* @author Conor Hayes * Filters message content using regular expressions. If a content mask is
*/ * provided message content will be altered.
public class ContentFilter { *
* @author Conor Hayes
private String patterns; */
public class ContentFilter {
private Collection<Pattern> compiledPatterns = new ArrayList<Pattern>();
private String patterns;
private String mask;
private Collection<Pattern> compiledPatterns = new ArrayList<Pattern>();
/** private String mask;
* A default instance will allow all message content.
*
* @see #setPatterns(String) /**
* @see #setMask(String) * A default instance will allow all message content.
*/ *
public ContentFilter() { * @see #setPatterns(String)
} * @see #setMask(String)
*/
/** public ContentFilter() {
* Set the patterns to use for searching content. }
*
* @param regExps a comma separated String of regular expressions /**
*/ * Set the patterns to use for searching content.
public void setPatterns(String patterns) { *
if (patterns != null) { * @param regExps a comma separated String of regular expressions
this.patterns = patterns; */
String[] data = patterns.split(","); public void setPatterns(String patterns) {
if (patterns != null) {
compiledPatterns.clear(); this.patterns = patterns;
String[] data = patterns.split(",");
for (int i = 0; i < data.length; i++) {
compiledPatterns.add(Pattern.compile(data[i])); compiledPatterns.clear();
}
} for (int i = 0; i < data.length; i++) {
else { compiledPatterns.add(Pattern.compile(data[i]));
clearPatterns(); }
} }
else {
} clearPatterns();
}
public String getPatterns() {
return this.patterns; }
}
public String getPatterns() {
/** return this.patterns;
* Clears all patterns. Calling this method means that all message content }
* will be allowed.
*/ /**
public void clearPatterns() { * Clears all patterns. Calling this method means that all message content
patterns = null; * will be allowed.
compiledPatterns.clear(); */
} public void clearPatterns() {
patterns = null;
/** compiledPatterns.clear();
* Set the content replacement mask. }
*
* @param mask the mask to use when replacing content /**
*/ * Set the content replacement mask.
public void setMask(String mask) { *
this.mask = mask; * @param mask the mask to use when replacing content
} */
public void setMask(String mask) {
/** this.mask = mask;
* @return the current mask or null if none has been set }
*/
public String getMask() { /**
return mask; * @return the current mask or null if none has been set
} */
public String getMask() {
/** return mask;
* Clears the content mask. }
*
* @see #filter(Message) /**
*/ * Clears the content mask.
public void clearMask() { *
mask = null; * @see #filter(Message)
} */
public void clearMask() {
mask = null;
/** }
* @return true if the filter is currently masking content, false otherwise
*/
public boolean isMaskingContent() { /**
return mask != null; * @return true if the filter is currently masking content, false otherwise
} */
public boolean isMaskingContent() {
/** return mask != null;
* Filters message content. }
*
* @param msg the message to filter, its subject/body may be altered if there /**
* are content matches and a content mask is set * Filters packet content.
* @return true if the msg content matched up, false otherwise *
*/ * @param packet the packet to filter, its content may be altered if there
public boolean filter(Message msg) { * are content matches and a content mask is set
boolean hasMatch = false; * @return true if the msg content matched up, false otherwise
*/
if (msg.getSubject() != null) { public boolean filter(Packet p) {
if (hasMatch(msg.getSubject())) { return process(p.getElement());
hasMatch = true; }
if (isMaskingContent()) {
String newSubject = replaceContent(msg.getSubject()); private boolean process(Element element) {
msg.setSubject(newSubject);
} boolean matched = mask(element);
}
} if (!matched || isMaskingContent())
{
if (msg.getBody() != null) { //only check children if no match has yet been found
if (hasMatch(msg.getBody())) { //or all content must be masked
hasMatch = true; Iterator iter = element.elementIterator();
if (isMaskingContent()) { while (iter.hasNext()) {
String newBody = replaceContent(msg.getBody()); matched |= process((Element)iter.next());
msg.setBody(newBody); }
} }
}
} return matched;
}
return hasMatch;
} private boolean mask(Element element) {
private String replaceContent(String content) { boolean match = false;
for (Pattern pattern : compiledPatterns) {
Matcher m = pattern.matcher(content); String content = element.getText();
content = m.replaceAll(mask);
} if ((content != null) && (content.length() > 0)) {
return content; for (Pattern pattern : compiledPatterns) {
}
Matcher matcher = pattern.matcher(content);
/**
* Performs sequential search for any pattern match. if (matcher.find()) {
*
* @param content the content to search against match = true;
* @return true if a match is found, false otherwise
*/ if (isMaskingContent()) {
private boolean hasMatch(String content) { content = matcher.replaceAll(mask);
boolean hasMatch = false; element.setText(content);
}
for (Pattern pattern : compiledPatterns) { }
Matcher m = pattern.matcher(content); }
}
if (m.find()) {
hasMatch = true; return match;
break; }
}
} /**
* Applies mask to the given <code>content</code>
return hasMatch; *
} * @param content
* @return masked content
*/
private String mask(String content) {
for (Pattern pattern : compiledPatterns) {
Matcher m = pattern.matcher(content);
content = m.replaceAll(mask);
}
return content;
}
/**
* Applies patterns against the given <code>content</code>. Terminates on
* first match.
*
* @param content the content to search against
* @return true if a match is found, false otherwise
*/
private boolean hasMatch(String content) {
boolean hasMatch = false;
for (Pattern pattern : compiledPatterns) {
Matcher matcher = pattern.matcher(content);
if (matcher.find()) {
hasMatch = true;
break;
}
}
return hasMatch;
}
} }
\ No newline at end of file
/** /**
* $RCSfile$ * $RCSfile$
* $Revision: 1594 $ * $Revision: 1594 $
* $Date: 2005-07-04 14:08:42 -0300 (Mon, 04 Jul 2005) $ * $Date: 2005-07-04 18:08:42 +0100 (Mon, 04 Jul 2005) $
* *
* Copyright (C) 2004 Jive Software. All rights reserved. * Copyright (C) 2004 Jive Software. All rights reserved.
* *
* This software is published under the terms of the GNU Public License (GPL), * This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution. * a copy of which is included in this distribution.
*/ */
package org.jivesoftware.wildfire.plugin; package org.jivesoftware.wildfire.plugin;
import org.jivesoftware.wildfire.MessageRouter; import java.io.File;
import org.jivesoftware.wildfire.Session; import java.util.ArrayList;
import org.jivesoftware.wildfire.XMPPServer; import java.util.List;
import org.jivesoftware.wildfire.container.Plugin;
import org.jivesoftware.wildfire.container.PluginManager; import javax.mail.internet.InternetAddress;
import org.jivesoftware.wildfire.interceptor.InterceptorManager; import javax.mail.internet.MimeMessage;
import org.jivesoftware.wildfire.interceptor.PacketInterceptor; import javax.mail.internet.MimeUtility;
import org.jivesoftware.wildfire.interceptor.PacketRejectedException;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.EmailService;
import org.xmpp.packet.JID; import org.jivesoftware.util.JiveGlobals;
import org.xmpp.packet.Message; import org.jivesoftware.util.Log;
import org.xmpp.packet.Packet; import org.jivesoftware.wildfire.MessageRouter;
import org.jivesoftware.wildfire.Session;
import java.io.File; import org.jivesoftware.wildfire.XMPPServer;
import org.jivesoftware.wildfire.container.Plugin;
/** import org.jivesoftware.wildfire.container.PluginManager;
* Content filter plugin. import org.jivesoftware.wildfire.interceptor.InterceptorManager;
* import org.jivesoftware.wildfire.interceptor.PacketInterceptor;
* @author Conor Hayes import org.jivesoftware.wildfire.interceptor.PacketRejectedException;
*/ import org.jivesoftware.wildfire.user.User;
public class ContentFilterPlugin implements Plugin, PacketInterceptor { import org.jivesoftware.wildfire.user.UserManager;
import org.xmpp.packet.JID;
/** import org.xmpp.packet.Message;
* The expected value is a boolean, if true the user identified by the value import org.xmpp.packet.Packet;
* of the property #VIOLATION_NOTIFICATION_CONTACT_PROPERTY will be notified import org.xmpp.packet.Presence;
* every time there is a content match, otherwise no notification will be
* sent. Then default value is false. /**
*/ * Content filter plugin.
public static final String VIOLATION_NOTIFICATION_ENABLED_PROPERTY = *
"plugin.contentFilter.violation.notification.enabled"; * @author Conor Hayes
*/
/** public class ContentFilterPlugin implements Plugin, PacketInterceptor {
* The expected value is a user name. The default value is "admin".
*/ /**
public static final String VIOLATION_NOTIFICATION_CONTACT_PROPERTY = * The expected value is a boolean, if true the user identified by the value
"plugin.contentFilter.violation.notification.contact"; * of the property #VIOLATION_NOTIFICATION_CONTACT_PROPERTY will be notified
* every time there is a content match, otherwise no notification will be
/** * sent. Then default value is false.
* The expected value is a boolean, if true the sender will be notified when a */
* message is rejected, otherwise the message will be silently rejected,i.e. the public static final String VIOLATION_NOTIFICATION_ENABLED_PROPERTY =
* sender will not know that the message was rejected and the receiver will "plugin.contentFilter.violation.notification.enabled";
* not get the message. The default value is false.
*/ /**
public static final String REJECTION_NOTIFICATION_ENABLED_PROPERTY = * The expected value is a user name. The default value is "admin".
"plugin.contentFilter.rejection.notification.enabled"; */
public static final String VIOLATION_NOTIFICATION_CONTACT_PROPERTY =
/** "plugin.contentFilter.violation.notification.contact";
* The expected value is a string, containing the desired message for the
* sender notification. /**
*/ * The expected value is a boolean, if true the user identified by the value
public static final String REJECTION_MSG_PROPERTY = "plugin.contentFilter.rejection.msg"; * of the property #VIOLATION_NOTIFICATION_CONTACT_PROPERTY, will also receive
* a copy of the offending packet. The default value is false.
/** */
* The expected value is a boolean, if true the value of #PATTERNS_PROPERTY public static final String VIOLATION_INCLUDE_ORIGNAL_PACKET_ENABLED_PROPERTY =
* will be used for pattern matching. "plugin.contentFilter.violation.notification.include.original.enabled";
*/
public static final String PATTERNS_ENABLED_PROPERTY = "plugin.contentFilter.patterns.enabled"; /**
* The expected value is a boolean, if true the user identified by the value
/** * of the property #VIOLATION_NOTIFICATION_CONTACT_PROPERTY, will receive
* The expected value is a comma separated string of regular expressions. * notification by IM. The default value is true.
*/ */
public static final String PATTERNS_PROPERTY = "plugin.contentFilter.patterns"; public static final String VIOLATION_NOTIFICATION_BY_IM_ENABLED_PROPERTY =
"plugin.contentFilter.violation.notification.by.im.enabled";
/**
* The expected value is a boolean, if true the value of #MASK_PROPERTY will /**
* be used to mask matching content. * The expected value is a boolean, if true the user identified by the value
*/ * of the property #VIOLATION_NOTIFICATION_CONTACT_PROPERTY, will receive
public static final String MASK_ENABLED_PROPERTY = "plugin.contentFilter.mask.enabled"; * notification by email. The default value is false.
*/
/** public static final String VIOLATION_NOTIFICATION_BY_EMAIL_ENABLED_PROPERTY =
* The expected value is a string. If this property is set any "plugin.contentFilter.violation.notification.by.email.enabled";
* matching content will not be rejected but masked with the given value.
* Setting a content mask means that property #SENDER_NOTIFICATION_ENABLED_PROPERTY /**
* is ignored. The default value is "**". * The expected value is a boolean, if true the sender will be notified when a
*/ * message is rejected, otherwise the message will be silently rejected,i.e. the
public static final String MASK_PROPERTY = "plugin.contentFilter.mask"; * sender will not know that the message was rejected and the receiver will
* not get the message. The default value is false.
/** */
* the hook into the inteceptor chain public static final String REJECTION_NOTIFICATION_ENABLED_PROPERTY =
*/ "plugin.contentFilter.rejection.notification.enabled";
private InterceptorManager interceptorManager;
/**
/** * The expected value is a string, containing the desired message for the
* used to send violation notifications * sender notification.
*/ */
private MessageRouter messageRouter; public static final String REJECTION_MSG_PROPERTY = "plugin.contentFilter.rejection.msg";
/** /**
* delegate that does the real work of this plugin * The expected value is a boolean, if true the value of #PATTERNS_PROPERTY
*/ * will be used for pattern matching.
private ContentFilter contentFilter; */
public static final String PATTERNS_ENABLED_PROPERTY = "plugin.contentFilter.patterns.enabled";
/**
* flags if sender should be notified of rejections /**
*/ * The expected value is a comma separated string of regular expressions.
private boolean rejectionNotificationEnabled; */
public static final String PATTERNS_PROPERTY = "plugin.contentFilter.patterns";
/**
* the rejection msg to send /**
*/ * The expected value is a boolean, if true Presence packets will
private String rejectionMessage; * be filtered
*/
/** public static final String FILTER_STATUS_ENABLED_PROPERTY = "plugin.contentFilter.filter.status.enabled";
* flags if content matches should result in admin notification
*/ /**
private boolean violationNotificationEnabled; * The expected value is a boolean, if true the value of #MASK_PROPERTY will
* be used to mask matching content.
/** */
* the admin user to send violation notifications to public static final String MASK_ENABLED_PROPERTY = "plugin.contentFilter.mask.enabled";
*/
private String violationContact; /**
* The expected value is a string. If this property is set any
/** * matching content will not be rejected but masked with the given value.
* flag if patterns should be used * Setting a content mask means that property #SENDER_NOTIFICATION_ENABLED_PROPERTY
*/ * is ignored. The default value is "**".
private boolean patternsEnabled; */
public static final String MASK_PROPERTY = "plugin.contentFilter.mask";
/**
* the patterns to use /**
*/ * the hook into the inteceptor chain
private String patterns; */
private InterceptorManager interceptorManager;
/**
* flag if mask should be used /**
*/ * used to send violation notifications
private boolean maskEnabled; */
private MessageRouter messageRouter;
/**
* the mask to use /**
*/ * delegate that does the real work of this plugin
private String mask; */
private ContentFilter contentFilter;
/**
* violation notification messages will be from this JID /**
*/ * flags if sender should be notified of rejections
private JID violationNotificationFrom; */
private boolean rejectionNotificationEnabled;
public ContentFilterPlugin() {
contentFilter = new ContentFilter(); /**
interceptorManager = InterceptorManager.getInstance(); * the rejection msg to send
violationNotificationFrom = new JID(XMPPServer.getInstance() */
.getServerInfo().getName()); private String rejectionMessage;
messageRouter = XMPPServer.getInstance().getMessageRouter();
} /**
* flags if content matches should result in admin notification
public boolean isMaskEnabled() { */
return maskEnabled; private boolean violationNotificationEnabled;
}
/**
public void setMaskEnabled(boolean enabled) { * the admin user to send violation notifications to
maskEnabled = enabled; */
JiveGlobals.setProperty(MASK_ENABLED_PROPERTY, enabled ? "true" : "false"); private String violationContact;
changeContentFilterMask(); /**
} * flags if original packet should be included in the message
* to the violation contact.
public void setMask(String mas) { */
mask = mas; private boolean violationIncludeOriginalPacketEnabled;
JiveGlobals.setProperty(MASK_PROPERTY, mas);
/**
changeContentFilterMask(); * flags if violation contact should be notified by IM.
} */
private boolean violationNotificationByIMEnabled;
private void changeContentFilterMask() {
if (maskEnabled) { /**
contentFilter.setMask(mask); * flags if violation contact should be notified by email.
} */
else { private boolean violationNotificationByEmailEnabled;
contentFilter.clearMask();
} /**
} * flag if patterns should be used
*/
public String getMask() { private boolean patternsEnabled;
return mask;
} /**
* the patterns to use
public boolean isPatternsEnabled() { */
return patternsEnabled; private String patterns;
}
/**
public void setPatternsEnabled(boolean enabled) { * flag if Presence packets should be filtered.
patternsEnabled = enabled; */
JiveGlobals.setProperty(PATTERNS_ENABLED_PROPERTY, enabled ? "true" private boolean filterStatusEnabled;
: "false");
/**
changeContentFilterPatterns(); * flag if mask should be used
} */
private boolean maskEnabled;
public void setPatterns(String patt) {
patterns = patt; /**
JiveGlobals.setProperty(PATTERNS_PROPERTY, patt); * the mask to use
*/
changeContentFilterPatterns(); private String mask;
}
/**
private void changeContentFilterPatterns() { * violation notification messages will be from this JID
if (patternsEnabled) { */
contentFilter.setPatterns(patterns); private JID violationNotificationFrom;
}
else { public ContentFilterPlugin() {
contentFilter.clearPatterns(); contentFilter = new ContentFilter();
} interceptorManager = InterceptorManager.getInstance();
} violationNotificationFrom = new JID(XMPPServer.getInstance()
.getServerInfo().getName());
public String getPatterns() { messageRouter = XMPPServer.getInstance().getMessageRouter();
return patterns; }
}
public boolean isMaskEnabled() {
public boolean isRejectionNotificationEnabled() { return maskEnabled;
return rejectionNotificationEnabled; }
}
public void setMaskEnabled(boolean enabled) {
public void setRejectionNotificationEnabled(boolean enabled) { maskEnabled = enabled;
rejectionNotificationEnabled = enabled; JiveGlobals.setProperty(MASK_ENABLED_PROPERTY, enabled ? "true" : "false");
JiveGlobals.setProperty(REJECTION_NOTIFICATION_ENABLED_PROPERTY,
enabled ? "true" : "false"); changeContentFilterMask();
} }
public String getRejectionMessage() { public void setMask(String mas) {
return rejectionMessage; mask = mas;
} JiveGlobals.setProperty(MASK_PROPERTY, mas);
public void setRejectionMessage(String message) { changeContentFilterMask();
this.rejectionMessage = message; }
JiveGlobals.setProperty(REJECTION_MSG_PROPERTY, message);
} private void changeContentFilterMask() {
if (maskEnabled) {
public boolean isViolationNotificationEnabled() { contentFilter.setMask(mask);
return violationNotificationEnabled; }
} else {
contentFilter.clearMask();
public void setViolationNotificationEnabled(boolean enabled) { }
violationNotificationEnabled = enabled; }
JiveGlobals.setProperty(VIOLATION_NOTIFICATION_ENABLED_PROPERTY,
enabled ? "true" : "false"); public String getMask() {
} return mask;
}
public void setViolationContact(String contact) {
violationContact = contact; public boolean isPatternsEnabled() {
JiveGlobals.setProperty(VIOLATION_NOTIFICATION_CONTACT_PROPERTY, contact); return patternsEnabled;
} }
public String getViolationContact() { public void setPatternsEnabled(boolean enabled) {
return violationContact; patternsEnabled = enabled;
} JiveGlobals.setProperty(PATTERNS_ENABLED_PROPERTY, enabled ? "true"
: "false");
public void initializePlugin(PluginManager pManager, File pluginDirectory) {
// configure this plugin changeContentFilterPatterns();
initFilter(); }
// register with interceptor manager public void setPatterns(String patt) {
interceptorManager.addInterceptor(this); patterns = patt;
} JiveGlobals.setProperty(PATTERNS_PROPERTY, patt);
private void initFilter() { changeContentFilterPatterns();
// default to false }
violationNotificationEnabled = JiveGlobals.getBooleanProperty(
VIOLATION_NOTIFICATION_ENABLED_PROPERTY, false); public boolean isFilterStatusEnabled() {
return filterStatusEnabled;
// default to "admin" }
violationContact = JiveGlobals.getProperty(VIOLATION_NOTIFICATION_CONTACT_PROPERTY,
"admin"); public void setFilterStatusEnabled(boolean enabled) {
filterStatusEnabled = enabled;
// default to false JiveGlobals.setProperty(FILTER_STATUS_ENABLED_PROPERTY, enabled ? "true"
rejectionNotificationEnabled = JiveGlobals.getBooleanProperty( : "false");
REJECTION_NOTIFICATION_ENABLED_PROPERTY, false); }
// default to english private void changeContentFilterPatterns() {
rejectionMessage = JiveGlobals.getProperty(REJECTION_MSG_PROPERTY, if (patternsEnabled) {
"Message rejected. This is an automated server response"); contentFilter.setPatterns(patterns);
}
// default to false else {
patternsEnabled = JiveGlobals.getBooleanProperty(PATTERNS_ENABLED_PROPERTY, contentFilter.clearPatterns();
false); }
}
//default to "fox,dog"
patterns = JiveGlobals.getProperty(PATTERNS_PROPERTY, "fox,dog"); public String getPatterns() {
return patterns;
changeContentFilterPatterns(); }
// default to false public boolean isRejectionNotificationEnabled() {
maskEnabled = JiveGlobals.getBooleanProperty(MASK_ENABLED_PROPERTY, false); return rejectionNotificationEnabled;
}
//default to "***"
mask = JiveGlobals.getProperty(MASK_PROPERTY, "***"); public void setRejectionNotificationEnabled(boolean enabled) {
rejectionNotificationEnabled = enabled;
changeContentFilterMask(); JiveGlobals.setProperty(REJECTION_NOTIFICATION_ENABLED_PROPERTY,
} enabled ? "true" : "false");
}
/**
* @see org.jivesoftware.wildfire.container.Plugin#destroyPlugin() public String getRejectionMessage() {
*/ return rejectionMessage;
public void destroyPlugin() { }
// unregister with interceptor manager
interceptorManager.removeInterceptor(this); public void setRejectionMessage(String message) {
} this.rejectionMessage = message;
JiveGlobals.setProperty(REJECTION_MSG_PROPERTY, message);
}
public void interceptPacket(Packet packet, Session session, boolean read,
boolean processed) throws PacketRejectedException { public boolean isViolationNotificationEnabled() {
if (patternsEnabled && !processed && (packet instanceof Message)) { return violationNotificationEnabled;
Message msg = (Message) packet; }
// filter the message public void setViolationNotificationEnabled(boolean enabled) {
boolean contentMatched = contentFilter.filter(msg); violationNotificationEnabled = enabled;
JiveGlobals.setProperty(VIOLATION_NOTIFICATION_ENABLED_PROPERTY,
// notify contact of violations enabled ? "true" : "false");
if (contentMatched && violationNotificationEnabled) { }
sendViolationNotification(msg);
} public void setViolationContact(String contact) {
violationContact = contact;
// reject the message if not masking content JiveGlobals.setProperty(VIOLATION_NOTIFICATION_CONTACT_PROPERTY, contact);
if (contentMatched && !maskEnabled) { }
PacketRejectedException rejected = new PacketRejectedException(
"Message rejected with disallowed content!"); public String getViolationContact() {
return violationContact;
if (rejectionNotificationEnabled) { }
// let the sender know about the rejection, this is
// only possible/useful if the content is not masked public boolean isViolationIncludeOriginalPacketEnabled() {
rejected.setRejectionMessage(rejectionMessage); return violationIncludeOriginalPacketEnabled;
} }
throw rejected; public void setViolationIncludeOriginalPacketEnabled(boolean enabled) {
} violationIncludeOriginalPacketEnabled = enabled;
} JiveGlobals.setProperty(VIOLATION_INCLUDE_ORIGNAL_PACKET_ENABLED_PROPERTY,
} enabled ? "true" : "false");
}
private void sendViolationNotification(Message offendingMsg) {
String subject = "Content filter notification!"; public boolean isViolationNotificationByIMEnabled() {
return violationNotificationByIMEnabled;
String msg = "Disallowed content detected in message from:" }
+ offendingMsg.getFrom() + " to:" + offendingMsg.getTo()
+ ", message was " public void setViolationNotificationByIMEnabled(boolean enabled) {
+ (contentFilter.isMaskingContent() ? "altered" : "rejected"); violationNotificationByIMEnabled = enabled;
JiveGlobals.setProperty(VIOLATION_NOTIFICATION_BY_IM_ENABLED_PROPERTY,
messageRouter.route(createServerMessage(subject, msg)); enabled ? "true" : "false");
} }
private Message createServerMessage(String subject, String body) { public boolean isViolationNotificationByEmailEnabled() {
Message message = new Message(); return violationNotificationByEmailEnabled;
message.setTo(violationContact + "@" }
+ violationNotificationFrom.getDomain());
message.setFrom(violationNotificationFrom); public void setViolationNotificationByEmailEnabled(boolean enabled) {
message.setSubject(subject); violationNotificationByEmailEnabled = enabled;
message.setBody(body); JiveGlobals.setProperty(VIOLATION_NOTIFICATION_BY_EMAIL_ENABLED_PROPERTY,
return message; enabled ? "true" : "false");
} }
public void initializePlugin(PluginManager pManager, File pluginDirectory) {
// configure this plugin
initFilter();
// register with interceptor manager
interceptorManager.addInterceptor(this);
}
private void initFilter() {
// default to false
violationNotificationEnabled = JiveGlobals.getBooleanProperty(
VIOLATION_NOTIFICATION_ENABLED_PROPERTY, false);
// default to "admin"
violationContact = JiveGlobals.getProperty(VIOLATION_NOTIFICATION_CONTACT_PROPERTY,
"admin");
// default to true
violationNotificationByIMEnabled = JiveGlobals.getBooleanProperty(
VIOLATION_NOTIFICATION_BY_IM_ENABLED_PROPERTY, true);
// default to false
violationNotificationByEmailEnabled = JiveGlobals.getBooleanProperty(
VIOLATION_NOTIFICATION_BY_EMAIL_ENABLED_PROPERTY, false);
// default to false
violationIncludeOriginalPacketEnabled = JiveGlobals.getBooleanProperty(
VIOLATION_INCLUDE_ORIGNAL_PACKET_ENABLED_PROPERTY, false);
// default to false
rejectionNotificationEnabled = JiveGlobals.getBooleanProperty(
REJECTION_NOTIFICATION_ENABLED_PROPERTY, false);
// default to english
rejectionMessage = JiveGlobals.getProperty(REJECTION_MSG_PROPERTY,
"Message rejected. This is an automated server response");
// default to false
patternsEnabled = JiveGlobals.getBooleanProperty(PATTERNS_ENABLED_PROPERTY,
false);
//default to "fox,dog"
patterns = JiveGlobals.getProperty(PATTERNS_PROPERTY, "fox,dog");
changeContentFilterPatterns();
// default to false
filterStatusEnabled = JiveGlobals.getBooleanProperty(FILTER_STATUS_ENABLED_PROPERTY,
false);
// default to false
maskEnabled = JiveGlobals.getBooleanProperty(MASK_ENABLED_PROPERTY, false);
//default to "***"
mask = JiveGlobals.getProperty(MASK_PROPERTY, "***");
changeContentFilterMask();
}
/**
* @see org.jivesoftware.wildfire.container.Plugin#destroyPlugin()
*/
public void destroyPlugin() {
// unregister with interceptor manager
interceptorManager.removeInterceptor(this);
}
public void interceptPacket(Packet packet, Session session, boolean read,
boolean processed) throws PacketRejectedException {
if (isValidTargetPacket(packet, read, processed)) {
Packet original = packet;
if (Log.isDebugEnabled()) {
Log.debug("Content filter: intercepted packet:" + original.toString());
}
// make a copy of the original packet only if required,
// as it's an expensive operation
if (violationNotificationEnabled && violationIncludeOriginalPacketEnabled && maskEnabled) {
original = packet.createCopy();
}
// filter the packet
boolean contentMatched = contentFilter.filter(packet);
if (Log.isDebugEnabled()) {
Log.debug("Content filter: content matched? " + contentMatched);
}
// notify admin of violations
if (contentMatched && violationNotificationEnabled) {
if (Log.isDebugEnabled()) {
Log.debug("Content filter: sending violation notification.");
Log.debug("Content filter: include original msg?" +
this.violationIncludeOriginalPacketEnabled);
}
sendViolationNotification(original);
}
// msg will either be rejected silently, rejected with
// some notification to sender, or masked.
if (contentMatched) {
if (maskEnabled) {
//masking enabled, no further action required
if (Log.isDebugEnabled()) {
Log.debug("Content filter: masked content:" + packet.toString());
}
} else {
//no masking, msg must be rejected
if (Log.isDebugEnabled()) {
Log.debug("Content filter: rejecting packet.");
}
PacketRejectedException rejected = new PacketRejectedException(
"Packet rejected with disallowed content!");
if (rejectionNotificationEnabled) {
// let the sender know about the rejection, this is
// only possible/useful if the content is not masked
rejected.setRejectionMessage(rejectionMessage);
}
throw rejected;
}
}
}
}
private boolean isValidTargetPacket(Packet packet, boolean read, boolean processed) {
return patternsEnabled && !processed && read &&
(packet instanceof Message ||
(filterStatusEnabled && packet instanceof Presence));
}
private void sendViolationNotification(Packet originalPacket) {
String subject = "Content filter notification!";
String body = null;
if (originalPacket instanceof Message) {
Message originalMsg = (Message) originalPacket;
body = "Disallowed content detected in message from:"
+ originalMsg.getFrom() + " to:" + originalMsg.getTo()
+ ", message was "
+ (contentFilter.isMaskingContent() ? "altered." : "rejected.")
+ (violationIncludeOriginalPacketEnabled ?
"\nOriginal subject:" + (originalMsg.getSubject() != null ? originalMsg.getSubject() : "")
+ "\nOriginal content:" + (originalMsg.getBody() != null ? originalMsg.getBody() : "") : "");
} else {
//presence
Presence originalPresence = (Presence) originalPacket;
body = "Disallowed status detected in presence from:"
+ originalPresence.getFrom()
+ ", status was "
+ (contentFilter.isMaskingContent() ? "altered." : "rejected.")
+ (violationIncludeOriginalPacketEnabled ?
"\nOriginal status:" + originalPresence.getStatus() : "");
}
if (violationNotificationByIMEnabled) {
if (Log.isDebugEnabled()) {
Log.debug("Sending IM notification");
}
messageRouter.route(createServerMessage(subject, body));
}
if (violationNotificationByEmailEnabled) {
if (Log.isDebugEnabled()) {
Log.debug("Sending email notification");
}
sendViolationNotificationEmail(subject, body);
}
}
private Message createServerMessage(String subject, String body) {
Message message = new Message();
message.setTo(violationContact + "@"
+ violationNotificationFrom.getDomain());
message.setFrom(violationNotificationFrom);
message.setSubject(subject);
message.setBody(body);
return message;
}
private void sendViolationNotificationEmail(String subject, String body) {
List<MimeMessage> messages = new ArrayList<MimeMessage>();
EmailService emailService = EmailService.getInstance();
MimeMessage message = emailService.createMimeMessage();
String encoding = MimeUtility.mimeCharset("iso-8859-1");
try {
User user = UserManager.getInstance().getUser(violationContact);
message.setRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(user.getEmail()));
message.setFrom(new InternetAddress("no_reply@" + violationNotificationFrom, "Wildfire", encoding));
message.setText(body);
message.setSubject(subject);
messages.add(message);
} catch (Exception e) {
Log.error(e);
}
emailService.sendMessages(messages);
}
} }
\ No newline at end of file
<%@ page import="java.util.*, <%@ page import="java.util.*,
org.jivesoftware.wildfire.XMPPServer, org.jivesoftware.wildfire.XMPPServer,
org.jivesoftware.wildfire.user.*, org.jivesoftware.wildfire.user.*,
org.jivesoftware.wildfire.plugin.ContentFilterPlugin, org.jivesoftware.wildfire.plugin.ContentFilterPlugin,
org.jivesoftware.util.*" org.jivesoftware.util.*"
%> %>
<%@ page import="java.util.regex.Pattern"%> <%@ page import="java.util.regex.Pattern"%>
...@@ -13,10 +13,12 @@ ...@@ -13,10 +13,12 @@
boolean save = request.getParameter("save") != null; boolean save = request.getParameter("save") != null;
boolean success = request.getParameter("success") != null; boolean success = request.getParameter("success") != null;
//pattern options //filter options
boolean patternsEnabled = ParamUtils.getBooleanParameter(request, "patternsenabled"); boolean patternsEnabled = ParamUtils.getBooleanParameter(request, "patternsenabled");
String patterns = ParamUtils.getParameter(request, "patterns"); String patterns = ParamUtils.getParameter(request, "patterns");
String [] filterStatusChecked = ParamUtils.getParameters(request, "filterstatus");
boolean filterStatusEnabled = filterStatusChecked.length > 0;
//mask options //mask options
boolean maskEnabled = ParamUtils.getBooleanParameter(request, "maskenabled"); boolean maskEnabled = ParamUtils.getBooleanParameter(request, "maskenabled");
String mask = ParamUtils.getParameter(request, "mask"); String mask = ParamUtils.getParameter(request, "mask");
...@@ -28,6 +30,10 @@ ...@@ -28,6 +30,10 @@
//notification options //notification options
boolean notificationEnabled = ParamUtils.getBooleanParameter(request, "notificationenabled"); boolean notificationEnabled = ParamUtils.getBooleanParameter(request, "notificationenabled");
String contactName = ParamUtils.getParameter(request, "contactname"); String contactName = ParamUtils.getParameter(request, "contactname");
List<String> notificationOptions = Arrays.asList(ParamUtils.getParameters(request, "notificationcb"));
boolean notificationByIMEnabled = notificationOptions.contains("notificationbyim");
boolean notificationByEmailEnabled = notificationOptions.contains("notificationbyemail");
boolean includeOriginalEnabled = notificationOptions.contains("notificationincludeoriginal");
//get handle to plugin //get handle to plugin
ContentFilterPlugin plugin = (ContentFilterPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("contentfilter"); ContentFilterPlugin plugin = (ContentFilterPlugin) XMPPServer.getInstance().getPluginManager().getPlugin("contentfilter");
...@@ -35,57 +41,70 @@ ...@@ -35,57 +41,70 @@
//input validation //input validation
Map<String, String> errors = new HashMap<String, String>(); Map<String, String> errors = new HashMap<String, String>();
if (save) { if (save) {
if (patterns == null) { if (patterns == null) {
errors.put("missingPatterns", "missingPatterns"); errors.put("missingPatterns", "missingPatterns");
} } else {
else { String[] data = patterns.split(",");
try {
String[] data = patterns.split(",");
try {
for (String aData : data) { for (String aData : data) {
Pattern.compile(aData); Pattern.compile(aData);
} }
} catch (java.util.regex.PatternSyntaxException e) { } catch (java.util.regex.PatternSyntaxException e) {
errors.put("patternSyntaxException", e.getMessage()); errors.put("patternSyntaxException", e.getMessage());
}
} }
}
if (mask == null) {
errors.put("missingMask", "missingMask");
}
if (mask == null) { if (rejectionMsg == null) {
errors.put("missingMask", "missingMask"); errors.put("missingRejectionMsg", "missingRejectionMsg");
} }
if (rejectionMsg == null) { if (contactName == null) {
errors.put("missingRejectionMsg", "missingRejectionMsg"); errors.put("missingContactName", "missingContactName");
} } else {
contactName = contactName.trim().toLowerCase();
if (contactName == null) { try {
errors.put("missingContactName", "missingContactName"); User user = UserManager.getInstance().getUser(contactName);
} else { if (notificationByEmailEnabled) {
contactName = contactName.trim().toLowerCase(); //verify that the user has an email address
try { if (user.getEmail() == null) {
UserManager.getInstance().getUser(contactName); errors.put("userEmailNotConfigured", "userEmailNotConfigured");
} catch (UserNotFoundException unfe) { }
errors.put("userNotFound", "userNotFound"); //verify that the email server is configured
} if (!JiveGlobals.getBooleanProperty("mail.configured", false)) {
} errors.put("mailServerNotConfigured", "mailServerNotConfigured");
}
}
} catch (UserNotFoundException unfe) {
errors.put("userNotFound", "userNotFound");
}
}
if (!notificationByIMEnabled && !notificationByEmailEnabled) {
errors.put("notificationFormatNotConfigured", "notificationFormatNotConfigured");
}
if (errors.size() == 0) { if (errors.size() == 0) {
plugin.setPatternsEnabled(patternsEnabled); plugin.setPatternsEnabled(patternsEnabled);
plugin.setPatterns(patterns); plugin.setPatterns(patterns);
plugin.setMaskEnabled(maskEnabled); plugin.setFilterStatusEnabled(filterStatusEnabled);
plugin.setMask(mask); plugin.setMaskEnabled(maskEnabled);
plugin.setViolationNotificationEnabled(notificationEnabled); plugin.setMask(mask);
plugin.setViolationContact(contactName); plugin.setViolationNotificationEnabled(notificationEnabled);
plugin.setRejectionNotificationEnabled(rejectionNotificationEnabled); plugin.setViolationContact(contactName);
plugin.setRejectionMessage(rejectionMsg); plugin.setViolationNotificationByIMEnabled(notificationByIMEnabled);
response.sendRedirect("contentfilter-props-edit-form.jsp?success=true"); plugin.setViolationNotificationByEmailEnabled(notificationByEmailEnabled);
return; plugin.setViolationIncludeOriginalPacketEnabled(includeOriginalEnabled);
} plugin.setRejectionNotificationEnabled(rejectionNotificationEnabled);
plugin.setRejectionMessage(rejectionMsg);
} response.sendRedirect("contentfilter-props-edit-form.jsp?success=true");
else { return;
}
} else {
patterns = plugin.getPatterns(); patterns = plugin.getPatterns();
mask = plugin.getMask(); mask = plugin.getMask();
contactName = plugin.getViolationContact(); contactName = plugin.getViolationContact();
...@@ -97,9 +116,13 @@ ...@@ -97,9 +116,13 @@
mask = plugin.getMask(); mask = plugin.getMask();
contactName = plugin.getViolationContact(); contactName = plugin.getViolationContact();
rejectionMsg = plugin.getRejectionMessage(); rejectionMsg = plugin.getRejectionMessage();
notificationByIMEnabled = plugin.isViolationNotificationByIMEnabled();
notificationByEmailEnabled = plugin.isViolationNotificationByEmailEnabled();
includeOriginalEnabled = plugin.isViolationIncludeOriginalPacketEnabled();
} }
patternsEnabled = plugin.isPatternsEnabled(); patternsEnabled = plugin.isPatternsEnabled();
filterStatusEnabled = plugin.isFilterStatusEnabled();
maskEnabled = plugin.isMaskEnabled(); maskEnabled = plugin.isMaskEnabled();
notificationEnabled = plugin.isViolationNotificationEnabled(); notificationEnabled = plugin.isViolationNotificationEnabled();
rejectionNotificationEnabled = plugin.isRejectionNotificationEnabled(); rejectionNotificationEnabled = plugin.isRejectionNotificationEnabled();
...@@ -158,28 +181,31 @@ Use the form below to edit content filter settings.<br> ...@@ -158,28 +181,31 @@ Use the form below to edit content filter settings.<br>
<table cellpadding="3" cellspacing="0" border="0" width="100%"> <table cellpadding="3" cellspacing="0" border="0" width="100%">
<tbody> <tbody>
<tr> <tr>
<td width="1%"> <td width="1%">
<input type="radio" name="patternsenabled" value="false" id="not01" <input type="radio" name="patternsenabled" value="false" id="not01"
<%= ((patternsEnabled) ? "" : "checked") %>> <%= ((patternsEnabled) ? "" : "checked") %>>
</td> </td>
<td width="99%"> <td width="99%">
<label for="not01"><b>Disabled</b></label> - Messages will not be filtered. <label for="not01"><b>Disabled</b></label> - Packets will not be filtered.
</td> </td>
</tr> </tr>
<tr> <tr>
<td width="1%"> <td width="1%">
<input type="radio" name="patternsenabled" value="true" id="not02" <input type="radio" name="patternsenabled" value="true" id="not02"
<%= ((patternsEnabled) ? "checked" : "") %>> <%= ((patternsEnabled) ? "checked" : "") %>>
</td> </td>
<td width="99%"> <td width="99%">
<label for="not02"><b>Enabled</b></label> - Messages will be filtered. <label for="not02"><b>Enabled</b></label> - Packets will be filtered.
</td> </td>
</tr> </tr>
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td align="left">Patterns:&nbsp; <td align="left">Patterns:&nbsp;</td>
<input type="text" size="100" maxlength="100" name="patterns" </tr>
value="<%= (patterns != null ? patterns : "") %>"> <tr>
<td>&nbsp;</td>
<td>
<textarea rows="10" cols="100" name="patterns"><%= (patterns != null ? patterns : "") %></textarea>
<% if (errors.containsKey("missingPatterns")) { %> <% if (errors.containsKey("missingPatterns")) { %>
<span class="jive-error-text"> <span class="jive-error-text">
<br>Please enter comma separated, regular expressions. <br>Please enter comma separated, regular expressions.
...@@ -189,8 +215,12 @@ Use the form below to edit content filter settings.<br> ...@@ -189,8 +215,12 @@ Use the form below to edit content filter settings.<br>
<br>Invalid regular expression: <%= errors.get("patternSyntaxException") %>. Please try again. <br>Invalid regular expression: <%= errors.get("patternSyntaxException") %>. Please try again.
</span> </span>
<% } %> <% } %>
</td> </td>
</tr> </tr>
<tr>
<td>&nbsp;</td>
<td><input type="checkbox" name="filterstatus" value="filterstatus" <%= filterStatusEnabled ? "checked" : "" %>>Filter users presence status.</input></td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
...@@ -203,7 +233,7 @@ Use the form below to edit content filter settings.<br> ...@@ -203,7 +233,7 @@ Use the form below to edit content filter settings.<br>
<div> <div>
<p> <p>
Enable this feature to alter message content when there is a pattern match. Enable this feature to alter packet content when there is a pattern match.
</p> </p>
<table cellpadding="3" cellspacing="0" border="0" width="100%"> <table cellpadding="3" cellspacing="0" border="0" width="100%">
...@@ -214,7 +244,7 @@ Use the form below to edit content filter settings.<br> ...@@ -214,7 +244,7 @@ Use the form below to edit content filter settings.<br>
<%= ((maskEnabled) ? "" : "checked") %>> <%= ((maskEnabled) ? "" : "checked") %>>
</td> </td>
<td width="99%"> <td width="99%">
<label for="not01"><b>Disabled</b></label> - Messages will be rejected. <label for="not01"><b>Disabled</b></label> - Packets will be rejected.
</td> </td>
</tr> </tr>
<tr> <tr>
...@@ -223,7 +253,7 @@ Use the form below to edit content filter settings.<br> ...@@ -223,7 +253,7 @@ Use the form below to edit content filter settings.<br>
<%= ((maskEnabled) ? "checked" : "") %>> <%= ((maskEnabled) ? "checked" : "") %>>
</td> </td>
<td width="99%"> <td width="99%">
<label for="not02"><b>Enabled</b></label> - Messages will be masked. <label for="not02"><b>Enabled</b></label> - Packets will be masked.
</td> </td>
</tr> </tr>
<tr> <tr>
...@@ -250,7 +280,7 @@ Use the form below to edit content filter settings.<br> ...@@ -250,7 +280,7 @@ Use the form below to edit content filter settings.<br>
<div> <div>
<p> <p>
Enable this feature to have the message sender notified whenever a message is rejected. Enable this feature to have the sender notified whenever a packet is rejected.
NB: This feature is only operational if "Content Mask" feature is disabled. NB: This feature is only operational if "Content Mask" feature is disabled.
</p> </p>
...@@ -322,19 +352,39 @@ Use the form below to edit content filter settings.<br> ...@@ -322,19 +352,39 @@ Use the form below to edit content filter settings.<br>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td align="left">Username:&nbsp; <td align="left">Username:&nbsp
<input type="text" size="20" maxlength="100" name="contactname" <input type="text" size="20" maxlength="100" name="contactname" value="<%= (contactName != null ? contactName : "") %>">@<%= XMPPServer.getInstance().getServerInfo().getName() %>
value="<%= (contactName != null ? contactName : "") %>">@<%= XMPPServer.getInstance().getServerInfo().getName() %> <% if (errors.containsKey("missingContactName")) { %>
<% if (errors.containsKey("missingContactName")) { %> <span class="jive-error-text">
<span class="jive-error-text"> <br>Please enter a username.
<br>Please enter a username. </span>
</span> <% } else if (errors.containsKey("userNotFound")) { %>
<% } else if (errors.containsKey("userNotFound")) { %> <span class="jive-error-text">
<span class="jive-error-text"> <br>Could not find user. Please try again.
<br>Could not find user. Please try again. </span>
</span> <% } %>
<% } %> </td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<input type="checkbox" name="notificationcb" value="notificationbyim" <%= notificationByIMEnabled ? "checked" : "" %>>Notify by IM.</input>
<input type="checkbox" name="notificationcb" value="notificationbyemail" <%= notificationByEmailEnabled ? "checked" : "" %>>Notify by Email.</input>
<input type="checkbox" name="notificationcb" value="notificationincludeoriginal" <%= includeOriginalEnabled ? "checked" : "" %>>Include original packet.</input>
<% if (errors.containsKey("mailServerNotConfigured")) { %>
<span class="jive-error-text">
<br>Error, sending an email will fail because the mail server is not setup. Please go to the <a href="/system-email.jsp">mail settings page</a> and set the mail host.
</span>
<% } else if (errors.containsKey("userEmailNotConfigured")) { %>
<span class="jive-error-text">
<br>Please configure <a href="/user-properties.jsp?username=<%= contactName %>"><%= contactName %>'s</a> email address.
</span>
<% } else if (errors.containsKey("notificationFormatNotConfigured")) { %>
<span class="jive-error-text">
<br>Users must be notified by IM and/or Email.
</span>
<% } %>
</td> </td>
</tr> </tr>
</tbody> </tbody>
......
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