Commit d1708df9 authored by Nate Putnam's avatar Nate Putnam Committed by nate

Performance fixes.

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@10107 b35dd754-fafc-0310-a699-88a17e54d16e
parent 3327c4e3
......@@ -27,17 +27,12 @@ public class PacketFilter {
public Rule findMatch(Packet packet) {
if (packet.getTo() == null || packet.getFrom() == null) return null;
String to = packet.getTo().toBareJID();
String from = packet.getFrom().toBareJID();
//TODO Would it be better to keep a local copy of the rules?
for (Rule rule : ruleManager.getRules()) {
if (!rule.isDisabled() &&
typeMatch(rule.getPackeType().toString(), packet) &&
sourceDestMatch(rule.getDestType(), rule.getDestination(), to) &&
sourceDestMatch(rule.getSourceType(), rule.getSource(), from)) {
typeMatch(rule.getPackeType(), packet) &&
sourceDestMatch(rule.getDestType(), rule.getDestination(), packet.getTo()) &&
sourceDestMatch(rule.getSourceType(), rule.getSource(), packet.getFrom())) {
return rule;
}
......@@ -45,30 +40,30 @@ public class PacketFilter {
return null;
}
private boolean typeMatch(String rulePacketType, Packet packet) {
private boolean typeMatch(Rule.PacketType rulePacketType, Packet packet) {
//Simple case. Any.
if (rulePacketType.equals(Rule.PacketType.Any.toString())) return true;
if (rulePacketType == Rule.PacketType.Any) return true;
else if (packet instanceof Message) {
Message message = (Message) packet;
if (rulePacketType.equals(Rule.PacketType.Message.toString())) {
if (rulePacketType == Rule.PacketType.Message) {
return true;
}
//Try some types.
else if (rulePacketType.equals(Rule.PacketType.MessageChat.toString())
&& message.getType().toString().equals("chat")) {
else if (rulePacketType == Rule.PacketType.MessageChat
&& message.getType() == Message.Type.chat) {
return true;
} else if (rulePacketType.equals(Rule.PacketType.MessageGroupChat.toString())
&& message.getType().toString().equals("groupchat")) {
} else if (rulePacketType == Rule.PacketType.MessageGroupChat
&& message.getType() == Message.Type.groupchat) {
return true;
}
return false;
} else if (packet instanceof Presence) {
if (rulePacketType.equals(Rule.PacketType.Presence.toString())) {
if (rulePacketType == Rule.PacketType.Presence) {
return true;
} else return false;
} else if (packet instanceof IQ) {
if (rulePacketType.equals(Rule.PacketType.Iq.toString())) {
if (rulePacketType == Rule.PacketType.Iq) {
return true;
} else return false;
}
......@@ -77,38 +72,36 @@ public class PacketFilter {
}
private boolean sourceDestMatch(String type, String ruleToFrom, String packetToFrom) {
if (type.equals(Rule.SourceDestType.Any.toString())) return true;
if (type.equals(Rule.SourceDestType.User.toString())) {
if (ruleToFrom.equals(packetToFrom)) {
private boolean sourceDestMatch(Rule.SourceDestType type, String ruleToFrom, JID packetToFrom) {
if (type == Rule.SourceDestType.Any) return true;
if (type == Rule.SourceDestType.User) {
if (ruleToFrom.equals(packetToFrom.toBareJID())) {
return true;
}
} else if (type.equals(Rule.SourceDestType.Group.toString())) {
} else if (type == Rule.SourceDestType.Group) {
return packetToFromGroup(ruleToFrom, packetToFrom);
} else if (type.equals(Rule.SourceDestType.Component.toString())) {
if (ruleToFrom.toLowerCase().equals(PacketFilterUtil.getComponent(packetToFrom).toLowerCase())) {
} else if (type == Rule.SourceDestType.Component) {
if (ruleToFrom.toLowerCase().equals(packetToFrom.getDomain().toLowerCase())) {
return true;
}
} else if (type.equals(Rule.SourceDestType.Other.toString())) {
} else if (type == Rule.SourceDestType.Other) {
if (matchUser(ruleToFrom, packetToFrom)) {
return true;
}
}
return false;
}
private boolean matchUser(String ruleToFrom, String packetToFrom) {
private boolean matchUser(String ruleToFrom, JID packetToFrom) {
boolean match = false;
//Escape the text so I get a rule to packet match.
packetToFrom = JID.unescapeNode(packetToFrom);
// String escapedPacketToFrom = JID.unescapeNode(packetToFrom.toBareJID().toString());
if (ruleToFrom.indexOf("*") == 0 && ruleToFrom.indexOf("@") == 1) {
if (PacketFilterUtil.getDomain(ruleToFrom).equals(PacketFilterUtil.getDomain(packetToFrom))) {
if (PacketFilterUtil.getDomain(ruleToFrom).equals(packetToFrom.getDomain().toString())) {
match = true;
}
} else {
if (ruleToFrom.equals(packetToFrom)) {
if (ruleToFrom.equals(packetToFrom.toBareJID())) {
match = true;
}
}
......@@ -116,15 +109,18 @@ public class PacketFilter {
}
private boolean packetToFromGroup(String rulegroup, String packetToFrom) {
Group group = PacketFilterUtil.getGroup(rulegroup);
private boolean packetToFromGroup(String rulegroup, JID packetToFrom) {
Group group = null;
try {
group = GroupManager.getInstance().getProvider().getGroup(rulegroup);
} catch (GroupNotFoundException e) {
e.printStackTrace();
}
if (group == null) {
return false;
} else {
for (JID jid : group.getMembers()) {
if (jid.toBareJID().equals(packetToFrom)) {
return true;
}
if (group.isUser(packetToFrom)) {
return true;
}
}
return false;
......
......@@ -20,9 +20,7 @@ public class PacketFilterPlugin implements Plugin, PacketInterceptor {
private static PluginManager pluginManager;
public PacketFilterPlugin() {
XMPPServer server = XMPPServer.getInstance();
interceptorManager = InterceptorManager.getInstance();
}
//Packet Filter
......@@ -58,6 +56,10 @@ public class PacketFilterPlugin implements Plugin, PacketInterceptor {
}
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed) throws PacketRejectedException {
if (processed) {
return;
}
Rule rule = pf.findMatch(packet);
if (rule != null) {
......
......@@ -5,50 +5,19 @@ import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.group.GroupNotFoundException;
import org.jivesoftware.openfire.user.UserManager;
import org.xmpp.packet.JID;
import java.util.Collection;
/*
Static util methods.
*/
public class PacketFilterUtil {
static String serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
public static boolean isGroup(String name) {
try {
GroupManager.getInstance().getGroup(name);
return true;
} catch (GroupNotFoundException e) {
return false;
}
}
public static Group getGroup(String name) {
Group group = null;
try {
group = GroupManager.getInstance().getProvider().getGroup(name);
} catch (GroupNotFoundException e) {
e.printStackTrace();
}
return group;
}
//Faster the better. This will break if virtual hosts is ever implemented.
public static boolean isLocalUser(String jid) {
Collection<String> users = UserManager.getUserProvider().getUsernames();
for (String username : users) {
if (jid.equals(username+"@"+serverName)) {
return true;
}
}
return false;
}
/*
Method to get the component part from a jid. The packet could
be from the component itself so just return.
be from the component itself so just return.
*/
public static String getComponent(String jid) {
public static String getDomain(String jid) {
if (jid.contains("@")) {
int atIndex = jid.indexOf("@");
return (jid.substring(atIndex+1,jid.length()));
......@@ -57,40 +26,4 @@ public class PacketFilterUtil {
return jid;
}
}
public static String getDomain(String jid) {
return getComponent(jid);
}
/*
Figure out if the packet is going to a component
*/
/* public static boolean isComponent(String jid) {
ComponentList cList = ComponentList.getInstance();
if (cList.getComponentName(jid) == null) {
return false;
}
else {
return true;
}
}
*/
/*
Make reasonably sure that the string is a valid
JID.
*/
/*public static boolean isJID(String jid) {
try {
JID _jid = new JID(jid);
}
catch (IllegalArgumentException e) {
Log.error(e);
return false;
}
return true;
} */
}
......@@ -43,7 +43,6 @@ public class ComponentList implements IQResultListener {
return componentMap.get(jid);
}
public void receivedAnswer(IQ packet) {
if (IQ.Type.result == packet.getType()) {
......@@ -62,7 +61,6 @@ public class ComponentList implements IQResultListener {
Log.warn("An answer to a previously sent IQ stanza was never received. Packet id: " + packetId);
}
public Collection<String> getComponentDomains() {
return routingTable.getComponentsDomains();
}
......
......@@ -17,23 +17,23 @@ public abstract class AbstractRule implements Rule {
private String description;
private String ruleId;
private String displayName;
private String sourceType;
private String destType;
private SourceDestType sourceType;
private SourceDestType destType;
public String getDestType() {
public SourceDestType getDestType() {
return destType;
}
public void setDestType(String destType) {
public void setDestType(SourceDestType destType) {
this.destType = destType;
}
public String getSourceType() {
public SourceDestType getSourceType() {
return sourceType;
}
public void setSourceType(String sourceType) {
public void setSourceType(SourceDestType sourceType) {
this.sourceType = sourceType;
}
......
......@@ -81,8 +81,7 @@ public class DbRuleManager {
rule = new Pass();
else if (ruleType.equals(Drop.class.getName()))
rule = new Drop();
else if (ruleType.equals(Redirect.class.getName()))
rule = new Redirect();
rule.setRuleId(rs.getString(2));
rule.setPacketType(Rule.PacketType.valueOf(rs.getString(3)));
......@@ -92,9 +91,9 @@ public class DbRuleManager {
rule.doLog(rs.getBoolean(7));
rule.setDescription(rs.getString(8));
rule.setOrder(rs.getInt(9));
rule.setSourceType(rs.getString(10));
rule.setDestType(rs.getString(11));
rule.setSourceType(Rule.SourceDestType.valueOf(rs.getString(10)));
rule.setDestType(Rule.SourceDestType.valueOf(rs.getString(11)));
rules.add(rule);
}
......@@ -163,13 +162,15 @@ public class DbRuleManager {
con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(GET_LAST_ORDERID);
rs = pstmt.executeQuery();
rs.next();
count = rs.getInt(1);
if (rs.next()) {
count = rs.getInt(1);
}
else {
count = 0;
}
} catch (SQLException sqle) {
Log.error(sqle);
//If error dataset is probably empty
return 0;
}
finally {
DbConnectionManager.closeConnection(pstmt, con);
......@@ -220,13 +221,7 @@ public class DbRuleManager {
}
rule.setOrder(order);
pstmt.setString(2, rule.getPackeType().toString());
if (rule.getDestination().contains(" ")) {
rule.setDestination(rule.getDestination().replace(" ", ""));
}
pstmt.setString(3, rule.getDestination());
if (rule.getSource().contains(" ")) {
rule.setSource(rule.getSource().replace(" ", ""));
}
pstmt.setString(4, rule.getSource());
pstmt.setString(5, rule.getClass().getName());
if (rule.isDisabled()) {
......@@ -242,8 +237,8 @@ public class DbRuleManager {
pstmt.setByte(7, new Byte("0"));
}
pstmt.setString(8, rule.getDescription());
pstmt.setString(9, rule.getSourceType());
pstmt.setString(10, rule.getDestType());
pstmt.setString(9, rule.getSourceType().toString());
pstmt.setString(10, rule.getDestType().toString());
pstmt.execute();
rules.clear();
......@@ -337,8 +332,8 @@ public class DbRuleManager {
rule.isDisabled(rs.getBoolean(7));
rule.doLog(rs.getBoolean(8));
rule.setDescription(rs.getString(9));
rule.setSourceType(rs.getString(10));
rule.setDestType(rs.getString(11));
rule.setSourceType(Rule.SourceDestType.valueOf(rs.getString(10)));
rule.setDestType(Rule.SourceDestType.valueOf(rs.getString(11)));
}
......@@ -393,8 +388,8 @@ public class DbRuleManager {
pstmt.setString(7, rule.getDescription());
pstmt.setInt(8, order);
pstmt.setString(9, rule.getSourceType());
pstmt.setString(10, rule.getDestType());
pstmt.setString(9, rule.getSourceType().toString());
pstmt.setString(10, rule.getDestType().toString());
pstmt.setInt(11, new Integer(rule.getRuleId()));
pstmt.executeUpdate();
......
package org.jivesoftware.openfire.plugin.rules;
import org.jivesoftware.openfire.SessionManager;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.plugin.PacketFilterUtil;
import org.jivesoftware.openfire.session.ClientSession;
import org.jivesoftware.util.Log;
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import java.util.Collection;
public class Redirect extends AbstractRule implements Rule {
public String getDisplayName() {
return "Redirect";
}
public Packet doAction(Packet packet) throws PacketRejectedException {
Message newPacket = null;
if (packet instanceof Message) {
if (getDestType().equals(Rule.SourceDestType.Group.toString())) {
Group sendGroup = PacketFilterUtil.getGroup(getDestination());
ClientSession clientSession;
for (JID jid : sendGroup.getMembers()) {
newPacket = (Message) packet.createCopy();
newPacket.setTo(jid);
sendPacket(newPacket);
}
}
else {
JID jid = new JID(getDestination());
newPacket = (Message) packet.createCopy();
newPacket.setFrom("test@machintosh.local");
newPacket.setTo(jid);
sendPacket(newPacket);
}
if (doLog()) {
Log.info("Redirecting from "+packet.getFrom()+" to "+packet.getTo());
}
}
throw new PacketRejectedException();
}
public boolean destMustMatch() {
return false;
}
private static void sendPacket(Packet packet) {
SessionManager sessionManager = XMPPServer.getInstance().getSessionManager();
ClientSession clientSession;
clientSession = sessionManager.getSession(packet.getTo());
Log.info("Sending to "+packet.getTo());
if (clientSession != null) {
Log.info("***** Session Not Null ******");
clientSession.process(packet);
}
}
}
......@@ -88,13 +88,13 @@ public interface Rule {
public void setDisplayName(String displayName);
public String getSourceType();
public SourceDestType getSourceType();
public void setSourceType(String sourceType);
public void setSourceType(SourceDestType sourceType);
public String getDestType();
public SourceDestType getDestType();
public void setDestType(String destType);
public void setDestType(SourceDestType destType);
public Packet doAction(Packet packet) throws PacketRejectedException;
......
......@@ -78,33 +78,33 @@
log = rule.doLog().toString();
description = rule.getDescription();
String destType = rule.getDestType();
String sourceType = rule.getSourceType();
Rule.SourceDestType destType = rule.getDestType();
Rule.SourceDestType sourceType = rule.getSourceType();
destJID = destination;
sourceJID = source;
if (destType.equals(Rule.SourceDestType.Any.toString())) {
if (destType == Rule.SourceDestType.Any) {
isDestAny = true;
} else if (destType.equals(Rule.SourceDestType.Group.toString())) {
} else if (destType == Rule.SourceDestType.Group) {
isDestGroup = true;
} else if (destType.equals(Rule.SourceDestType.Component.toString())) {
} else if (destType == Rule.SourceDestType.Component) {
isDestComponent = true;
} else if (destType.equals(Rule.SourceDestType.User.toString())) {
} else if (destType == Rule.SourceDestType.User) {
isDestUser = true;
} else if (destType.equals(Rule.SourceDestType.Other.toString())) {
} else if (destType == Rule.SourceDestType.Other) {
isDestOther = true;
}
if (sourceType.equals(Rule.SourceDestType.Any.toString())) {
if (sourceType == Rule.SourceDestType.Any) {
isSourceAny = true;
} else if (sourceType.equals(Rule.SourceDestType.Group.toString())) {
} else if (sourceType == Rule.SourceDestType.Group) {
isSourceGroup = true;
} else if (sourceType.equals(Rule.SourceDestType.Component.toString())) {
} else if (sourceType == Rule.SourceDestType.Component) {
isSourceComponent = true;
} else if (sourceType.equals(Rule.SourceDestType.User.toString())) {
} else if (sourceType == Rule.SourceDestType.User) {
isSourceUser = true;
} else if (sourceType.equals(Rule.SourceDestType.Other.toString())) {
} else if (sourceType == Rule.SourceDestType.Other) {
isSourceOther = true;
}
......@@ -139,7 +139,7 @@
rule.setPacketType(Rule.PacketType.valueOf(packetType));
if (source.equals(Rule.SourceDestType.Any.toString())) {
rule.setSource(source);
rule.setSourceType(Rule.SourceDestType.Any.toString());
rule.setSourceType(Rule.SourceDestType.Any);
} else if (source.equals(Rule.SourceDestType.Other.toString())) {
sourceJID = ParamUtils.getParameter(request, "sourceOtherJID");
if (sourceJID == null || !(sourceJID.length() > 0)) {
......@@ -147,25 +147,25 @@
errors.put("sourceOther", "");
}
rule.setSource(sourceJID);
rule.setSourceType(Rule.SourceDestType.Other.toString());
rule.setSourceType(Rule.SourceDestType.Other);
} else if (source.equals(Rule.SourceDestType.User.toString())) {
sourceJID = ParamUtils.getParameter(request, "sourceUserJID");
rule.setSource(sourceJID);
rule.setSourceType(Rule.SourceDestType.User.toString());
rule.setSourceType(Rule.SourceDestType.User);
} else if (source.equals(Rule.SourceDestType.Group.toString())) {
sourceJID = ParamUtils.getParameter(request, "sourceGroupJID");
rule.setSource(sourceJID);
rule.setSourceType(Rule.SourceDestType.Group.toString());
rule.setSourceType(Rule.SourceDestType.Group);
} else if (source.equals(Rule.SourceDestType.Component.toString())) {
sourceJID = ParamUtils.getParameter(request, "sourceComponentJID");
rule.setSource(sourceJID);
rule.setSourceType(Rule.SourceDestType.Component.toString());
rule.setSourceType(Rule.SourceDestType.Component);
}
if (destination.equals(Rule.SourceDestType.Any.toString())) {
rule.setDestination(destination);
rule.setDestType(Rule.SourceDestType.Any.toString());
rule.setDestType(Rule.SourceDestType.Any);
} else if (destination.equals(Rule.SourceDestType.Other.toString())) {
destJID = ParamUtils.getParameter(request, "destOtherJID");
if (destJID == null || !(sourceJID.length() > 0)) {
......@@ -173,19 +173,19 @@
errors.put("destOther", "");
}
rule.setDestination(destJID);
rule.setDestType(Rule.SourceDestType.Other.toString());
rule.setDestType(Rule.SourceDestType.Other);
} else if (destination.equals(Rule.SourceDestType.User.toString())) {
destJID = ParamUtils.getParameter(request, "destUserJID");
rule.setDestination(destJID);
rule.setDestType(Rule.SourceDestType.User.toString());
rule.setDestType(Rule.SourceDestType.User);
} else if (destination.equals(Rule.SourceDestType.Group.toString())) {
destJID = ParamUtils.getParameter(request, "destGroupJID");
rule.setDestination(destJID);
rule.setDestType(Rule.SourceDestType.Group.toString());
rule.setDestType(Rule.SourceDestType.Group);
} else if (destination.equals(Rule.SourceDestType.Component.toString())) {
destJID = ParamUtils.getParameter(request, "destComponentJID");
rule.setDestination(destJID);
rule.setDestType(Rule.SourceDestType.Component.toString());
rule.setDestType(Rule.SourceDestType.Component);
}
......@@ -195,8 +195,21 @@
rule.setRuleId(request.getParameter("ruleId"));
rule.setOrder(new Integer(order));
if (errors.isEmpty()) {
rule.setSource(rule.getSource().toLowerCase());
if (rule.getSourceType() == Rule.SourceDestType.User ||
rule.getDestType() == Rule.SourceDestType.Other) {
rule.setSource(rule.getSource().toLowerCase());
}
else {
rule.setSource(rule.getSource());
}
if (rule.getDestType() == Rule.SourceDestType.User ||
rule.getDestType() == Rule.SourceDestType.Other) {
rule.setDestination(rule.getDestination().toLowerCase());
}
else {
rule.setDestination(rule.getDestination());
}
rm.updateRule(rule);
response.sendRedirect("pf-main.jsp");
}
......
......@@ -65,7 +65,7 @@
rule.setPacketType(Rule.PacketType.valueOf(packetType));
if (source.equals(Rule.SourceDestType.Any.toString())) {
rule.setSource(source);
rule.setSourceType(Rule.SourceDestType.Any.toString());
rule.setSourceType(Rule.SourceDestType.Any);
} else if (source.equals(Rule.SourceDestType.Other.toString())) {
sourceJID = ParamUtils.getParameter(request, "sourceOtherJID");
if (sourceJID == null || !(sourceJID.length() > 0)) {
......@@ -73,25 +73,25 @@
errors.put("sourceOther", "");
}
rule.setSource(sourceJID);
rule.setSourceType(Rule.SourceDestType.Other.toString());
rule.setSourceType(Rule.SourceDestType.Other);
} else if (source.equals(Rule.SourceDestType.User.toString())) {
sourceJID = ParamUtils.getParameter(request, "sourceUserJID");
rule.setSource(sourceJID);
rule.setSourceType(Rule.SourceDestType.User.toString());
rule.setSourceType(Rule.SourceDestType.User);
} else if (source.equals(Rule.SourceDestType.Group.toString())) {
sourceJID = ParamUtils.getParameter(request, "sourceGroupJID");
rule.setSource(sourceJID);
rule.setSourceType(Rule.SourceDestType.Group.toString());
rule.setSourceType(Rule.SourceDestType.Group);
} else if (source.equals(Rule.SourceDestType.Component.toString())) {
sourceJID = ParamUtils.getParameter(request, "sourceComponentJID");
rule.setSource(sourceJID);
rule.setSourceType(Rule.SourceDestType.Component.toString());
rule.setSourceType(Rule.SourceDestType.Component);
}
if (destination.equals(Rule.SourceDestType.Any.toString())) {
rule.setDestination(destination);
rule.setDestType(Rule.SourceDestType.Any.toString());
rule.setDestType(Rule.SourceDestType.Any);
} else if (destination.equals(Rule.SourceDestType.Other.toString())) {
destJID = ParamUtils.getParameter(request, "destOtherJID");
if (destJID == null || !(destJID.length() > 0)) {
......@@ -99,27 +99,40 @@
errors.put("destOther", "");
}
rule.setDestination(destJID);
rule.setDestType(Rule.SourceDestType.Other.toString());
rule.setDestType(Rule.SourceDestType.Other);
} else if (destination.equals(Rule.SourceDestType.User.toString())) {
destJID = ParamUtils.getParameter(request, "destUserJID");
rule.setDestination(destJID);
rule.setDestType(Rule.SourceDestType.User.toString());
rule.setDestType(Rule.SourceDestType.User);
} else if (destination.equals(Rule.SourceDestType.Group.toString())) {
destJID = ParamUtils.getParameter(request, "destGroupJID");
rule.setDestination(destJID);
rule.setDestType(Rule.SourceDestType.Group.toString());
rule.setDestType(Rule.SourceDestType.Group);
} else if (destination.equals(Rule.SourceDestType.Component.toString())) {
destJID = ParamUtils.getParameter(request, "destComponentJID");
rule.setDestination(destJID);
rule.setDestType(Rule.SourceDestType.Component.toString());
rule.setDestType(Rule.SourceDestType.Component);
}
rule.doLog(new Boolean(log).booleanValue());
rule.isDisabled(new Boolean(disable).booleanValue());
if (errors.size() == 0) {
rule.setSource(rule.getSource().toLowerCase());
if (rule.getSourceType() == Rule.SourceDestType.User ||
rule.getDestType() == Rule.SourceDestType.Other ) {
rule.setSource(rule.getSource().toLowerCase());
}
else {
rule.setSource(rule.getSource());
}
if (rule.getDestType() == Rule.SourceDestType.User ||
rule.getDestType() == Rule.SourceDestType.Other) {
rule.setDestination(rule.getDestination().toLowerCase());
}
else {
rule.setDestination(rule.getDestination());
}
rm.addRule(rule);
response.sendRedirect("pf-main.jsp");
}
......
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