tiny fixes here and there
[mir.git] / source / mircoders / global / Abuse.java
index 1ee0f01..dd54988 100755 (executable)
 
 package mircoders.global;
 
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-import java.util.Vector;
-
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletResponse;
-
 import mir.config.MirPropertiesConfiguration;
 import mir.entity.Entity;
+import mir.entity.adapter.EntityAdapterModel;
 import mir.log.LoggerWrapper;
 import mir.session.Request;
-import mir.util.DateTimeFunctions;
+import mir.util.DateTimeRoutines;
+import mir.util.EntityUtility;
 import mir.util.GeneratorFormatAdapters;
 import mir.util.StringRoutines;
+import mircoders.abuse.FilterEngine;
 import mircoders.entity.EntityComment;
 import mircoders.entity.EntityContent;
 import mircoders.localizer.MirAdminInterfaceLocalizer;
-import mircoders.localizer.MirAntiAbuseFilterType;
-
+import mircoders.module.ModuleComment;
+import mircoders.module.ModuleContent;
 import org.apache.commons.collections.ExtendedProperties;
 
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
 
+/**
+ *  This class manages abuse (spam, offending material, etc.). This
+ *  is done by using a set of filters managed by the FilterEngine class.
+ *  Filters may be of different types (IP, throttle, regexp...), 
+ *  but are created and configured in a single user interface (web page),
+ *  and are stored in a single database table called "filter". 
+ */
 public class Abuse {
-  private List filterRules;
-  private Map filterTypes;
-  private List filterTypeIds;
-  private int maxIdentifier;
   private LoggerWrapper logger;
   private int logSize;
   private boolean logEnabled;
@@ -76,18 +79,21 @@ public class Abuse {
   private String articleBlockAction;
   private String commentBlockAction;
   private List log;
-  private String configFile = MirGlobal.config().getStringWithHome("Abuse.Config");
+  private File configFile = MirGlobal.config().getFile("Abuse.Config");
+  private FilterEngine filterEngine;
 
   private MirPropertiesConfiguration configuration;
 
   private static String cookieName = MirGlobal.config().getString("Abuse.CookieName");
   private static int cookieMaxAge = 60 * 60 * MirGlobal.config().getInt("Abuse.CookieMaxAge");
+  private EntityAdapterModel model;
 
-  public Abuse() {
+  public Abuse(EntityAdapterModel aModel) {
     logger = new LoggerWrapper("Global.Abuse");
-    filterRules = new Vector();
-    maxIdentifier = 0;
-    log = new Vector();
+    filterEngine = new FilterEngine(aModel);
+    model = aModel;
+
+    log = new ArrayList();
 
     try {
       configuration = MirPropertiesConfiguration.instance();
@@ -104,25 +110,13 @@ public class Abuse {
     openPostingDisabled = false;
     cookieOnBlock = false;
 
-    try {
-      filterTypes = new HashMap();
-      filterTypeIds = new Vector();
-
-      Iterator i = MirGlobal.localizer().openPostings().getAntiAbuseFilterTypes().iterator();
-
-      while (i.hasNext()) {
-        MirAntiAbuseFilterType filterType = (MirAntiAbuseFilterType) i.next();
-        filterTypes.put(filterType.getName(), filterType);
-        filterTypeIds.add(filterType.getName());
-      }
-    }
-    catch (Throwable t) {
-      throw new RuntimeException("Can't get filter types: " + t.getMessage());
-    }
-
     load();
   }
 
+  public FilterEngine getFilterEngine() {
+    return filterEngine;
+  }
+
   private void setCookie(HttpServletResponse aResponse) {
     Random random = new Random();
 
@@ -150,96 +144,84 @@ public class Abuse {
 
     return false;
   }
-
-  FilterRule findMatchingFilter(Entity anEntity, Request aRequest) {
-    Iterator iterator = filterRules.iterator();
-
-    while (iterator.hasNext()) {
-      FilterRule rule = (FilterRule) iterator.next();
-
-      if (rule.test(anEntity, aRequest))
-        return rule;
-    }
-
-    return null;
-  }
-
+  /** Checks if there is a filter that matches a comment and takes 
+   * appropriate action (as configured in the xxxxxaction field of 
+   * the filter table). The actual matching is delegated to the 
+   * FilterEngine class. 
+   */
   public void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) {
     try {
       long time = System.currentTimeMillis();
 
-      FilterRule filterRule = findMatchingFilter(aComment, aRequest);
+      FilterEngine.Filter matchingFilter = filterEngine.testPosting(aComment, aRequest);
 
-      if (filterRule != null) {
-        logger.debug("Match for " + filterRule.getType() + " rule '" + filterRule.getExpression() + "'");
-        filterRule.setLastHit(new GregorianCalendar().getTime());
+      if (matchingFilter != null) {
+        logger.debug("Match for " + matchingFilter.getTag());
+        matchingFilter.updateLastHit(new GregorianCalendar().getTime());
 
         StringBuffer line = new StringBuffer();
 
-        line.append(DateTimeFunctions.advancedDateFormat(
+        line.append(DateTimeRoutines.advancedDateFormat(
             configuration.getString("Mir.DefaultDateTimeFormat"),
             (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));
 
         line.append(" ");
-        line.append("filter");
-
-        line.append(" ");
-        line.append(filterRule.getType() +" ("+ filterRule.getExpression()+")");
-        aComment.appendLineToField("comment", line.toString());
+        line.append(matchingFilter.getTag());
+        EntityUtility.appendLineToField(aComment, "comment", line.toString());
 
-        MirGlobal.performCommentOperation(null, aComment, filterRule.getCommentAction());
+        MirGlobal.performCommentOperation(null, aComment, matchingFilter.getCommentAction());
         setCookie(aResponse);
         save();
-        logComment(aComment, aRequest, filterRule.getType(), filterRule.getExpression());
+        logComment(aComment, aRequest, matchingFilter.getTag());
       }
-      else
+      else {
         logComment(aComment, aRequest);
+      }
 
-
-      logger.info("checkComment: " + (System.currentTimeMillis() - time) + "ms");
+      logger.debug("checkComment: " + (System.currentTimeMillis() - time) + "ms");
     }
     catch (Throwable t) {
-      t.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
-      logger.error("Abuse.checkComment: " + t.toString());
+      logger.error("Exception thrown while checking comment", t);
     }
   }
-
+  /** Checks if there is a filter that matches an articleand takes 
+   * appropriate action (as configured in the xxxxxaction field of 
+   * the filter table). The actual matching is delegated to the 
+   * FilterEngine class. 
+   */
   public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {
     try {
       long time = System.currentTimeMillis();
 
-      FilterRule filterRule = findMatchingFilter(anArticle, aRequest);
+      FilterEngine.Filter matchingFilter = filterEngine.testPosting(anArticle, aRequest);
 
-      if (filterRule != null) {
-        logger.debug("Match for " + filterRule.getType() + " rule '" + filterRule.getExpression() + "'");
-        filterRule.setLastHit(new GregorianCalendar().getTime());
+      if (matchingFilter != null) {
+        logger.debug("Match for " + matchingFilter.getTag());
+//        matchingFilter.updateLastHit(new GregorianCalendar().getTime());
 
         StringBuffer line = new StringBuffer();
 
-        line.append(DateTimeFunctions.advancedDateFormat(
+        line.append(DateTimeRoutines.advancedDateFormat(
             configuration.getString("Mir.DefaultDateTimeFormat"),
             (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));
 
         line.append(" ");
-        line.append("filter");
-
-        line.append(" ");
-        line.append(filterRule.getType() +" ("+ filterRule.getExpression()+")");
-        anArticle.appendLineToField("comment", line.toString());
+        line.append(matchingFilter.getTag());
+        EntityUtility.appendLineToField(anArticle, "comment", line.toString());
 
-        MirGlobal.performArticleOperation(null, anArticle, filterRule.getArticleAction());
+        MirGlobal.performArticleOperation(null, anArticle, matchingFilter.getArticleAction());
         setCookie(aResponse);
         save();
-        logArticle(anArticle, aRequest, filterRule.getType(), filterRule.getExpression());
+        logArticle(anArticle, aRequest, matchingFilter.getTag());
       }
-      else
+      else {
         logArticle(anArticle, aRequest);
+      }
 
       logger.info("checkArticle: " + (System.currentTimeMillis() - time) + "ms");
     }
     catch (Throwable t) {
-      t.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
-      logger.error("Abuse.checkArticle: " + t.toString());
+      logger.error("Exception thrown while checking article", t);
     }
   }
 
@@ -303,9 +285,12 @@ public class Abuse {
   }
 
   public List getLog() {
+    ModuleContent contentModule = new ModuleContent();
+    ModuleComment commentModule = new ModuleComment();
+
     synchronized (log) {
       try {
-        List result = new Vector();
+        List result = new ArrayList();
 
         Iterator i = log.iterator();
         while (i.hasNext()) {
@@ -315,13 +300,20 @@ public class Abuse {
           entry.put("ip", logEntry.getIpNumber());
           entry.put("id", logEntry.getId());
           entry.put("timestamp", new GeneratorFormatAdapters.DateFormatAdapter(logEntry.getTimeStamp(), MirPropertiesConfiguration.instance().getString("Mir.DefaultTimezone")));
-          if (logEntry.getIsArticle())
+
+          if (logEntry.getIsArticle()) {
             entry.put("type", "content");
-          else
+            entry.put("object",
+                model.makeEntityAdapter("content", contentModule.getById(logEntry.getId())));
+          }
+          else {
             entry.put("type", "comment");
+            entry.put("object",
+                model.makeEntityAdapter("comment", commentModule.getById(logEntry.getId())));
+          }
+
           entry.put("browser", logEntry.getBrowserString());
-          entry.put("hitfiltertype", logEntry.getHitFilterType());
-          entry.put("hitfilterexpression", logEntry.getHitFilterExpression());
+          entry.put("filtertag", logEntry.getMatchingFilterTag());
 
           result.add(entry);
         }
@@ -335,112 +327,82 @@ public class Abuse {
   }
 
   public void logComment(Entity aComment, Request aRequest) {
-    logComment(aComment, aRequest, null, null);
+    logComment(aComment, aRequest, null);
   }
 
-  public void logComment(Entity aComment, Request aRequest, String aHitFilterType, String aHitFilterExpression) {
+  public void logComment(Entity aComment, Request aRequest, String aMatchingFilterTag) {
     String ipAddress = aRequest.getHeader("ip");
     String id = aComment.getId();
     String browser = aRequest.getHeader("User-Agent");
 
-    logComment(ipAddress, id, new Date(), browser, aHitFilterType, aHitFilterExpression);
+    logComment(ipAddress, id, new Date(), browser, aMatchingFilterTag);
   }
 
   public void logArticle(Entity anArticle, Request aRequest) {
-    logArticle(anArticle, aRequest, null, null);
+    logArticle(anArticle, aRequest, null);
   }
 
-  public void logArticle(Entity anArticle, Request aRequest, String aHitFilterType, String aHitFilterExpression) {
+  public void logArticle(Entity anArticle, Request aRequest, String aMatchingFilterTag) {
     String ipAddress = aRequest.getHeader("ip");
     String id = anArticle.getId();
     String browser = aRequest.getHeader("User-Agent");
 
-    logArticle(ipAddress, id, new Date(), browser, aHitFilterType, aHitFilterExpression);
+    logArticle(ipAddress, id, new Date(), browser, aMatchingFilterTag);
   }
 
-  public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser, String aHitFilterType, String aHitFilterExpression) {
-    appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false, aHitFilterType, aHitFilterExpression));
+  public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser, String aMatchingFilterTag) {
+    appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false, aMatchingFilterTag));
   }
 
-  public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser, String aHitFilterType, String aHitFilterExpression) {
-    appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true, aHitFilterType, aHitFilterExpression));
+  public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser, String aMatchingFilterTag) {
+    appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true, aMatchingFilterTag));
   }
 
-  public void load() {
-    synchronized (filterRules) {
-      try {
-        ExtendedProperties configuration = new ExtendedProperties();
-
-        try {
-          configuration = new ExtendedProperties(configFile);
-        }
-        catch (FileNotFoundException e) {
-        }
-
-        getFilterConfig(filterRules, "abuse.filter", configuration);
-
-        setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));
-        setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));
-        setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));
-        setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));
-        setLogSize(configuration.getInt("abuse.logSize", 10));
-        setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));
-        setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));
-      }
-      catch (Throwable t) {
-        throw new RuntimeException(t.toString());
-      }
-    }
-  }
+  public synchronized void load() {
+    try {
+      ExtendedProperties configuration = new ExtendedProperties();
 
-  public void save() {
-    synchronized (filterRules) {
       try {
-        ExtendedProperties configuration = new ExtendedProperties();
-
-        setFilterConfig(filterRules, "abuse.filter", configuration);
-
-        configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled() ? "1" : "0");
-        configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword() ? "1" : "0");
-        configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock() ? "1" : "0");
-        configuration.addProperty("abuse.logEnabled", getLogEnabled() ? "1" : "0");
-        configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));
-        configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());
-        configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());
-
-        configuration.save(new BufferedOutputStream(new FileOutputStream(new File(configFile)),8192), "Anti abuse configuration");
+        configuration = new ExtendedProperties(configFile.getAbsolutePath());
       }
-      catch (Throwable t) {
-        throw new RuntimeException(t.toString());
+      catch (FileNotFoundException e) {
       }
+
+      setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));
+      setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));
+      setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));
+      setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));
+      setLogSize(configuration.getInt("abuse.logSize", 10));
+      setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));
+      setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));
+    }
+    catch (Throwable t) {
+      throw new RuntimeException(t.toString());
     }
   }
 
-  public List getFilterTypes() {
+  public synchronized void save() {
     try {
-      List result = new Vector();
+      ExtendedProperties configuration = new ExtendedProperties();
 
-      Iterator i = filterTypeIds.iterator();
-      while (i.hasNext()) {
-        String id = (String) i.next();
+      configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled() ? "1" : "0");
+      configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword() ? "1" : "0");
+      configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock() ? "1" : "0");
+      configuration.addProperty("abuse.logEnabled", getLogEnabled() ? "1" : "0");
+      configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));
+      configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());
+      configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());
 
-        Map action = new HashMap();
-        action.put("resource", id);
-        action.put("identifier", id);
-
-        result.add(action);
-      }
-
-      return result;
+      configuration.save(new BufferedOutputStream(new FileOutputStream(configFile),8192), "Anti abuse configuration");
     }
     catch (Throwable t) {
-      throw new RuntimeException("can't get article actions");
+      throw new RuntimeException(t.toString());
     }
   }
 
   public List getArticleActions() {
     try {
-      List result = new Vector();
+      List result = new ArrayList();
 
       Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();
       while (i.hasNext()) {
@@ -463,7 +425,7 @@ public class Abuse {
 
   public List getCommentActions() {
     try {
-      List result = new Vector();
+      List result = new ArrayList();
 
       Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();
       while (i.hasNext()) {
@@ -484,258 +446,16 @@ public class Abuse {
     }
   }
 
-  public List getFilters() {
-    List result = new Vector();
-
-    synchronized (filterRules) {
-      Iterator i = filterRules.iterator();
-      while (i.hasNext()) {
-        FilterRule filter = (FilterRule) i.next();
-        result.add(filter.clone());
-      }
-      return result;
-    }
-  }
-
-  public String addFilter(String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {
-    return addFilter(aType, anExpression, aComments, aCommentAction, anArticleAction, null);
-  }
-
-  public String addFilter(String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction, Date aListHit) {
-    return addFilter(filterRules, aType, anExpression, aComments, aCommentAction, anArticleAction, aListHit);
-  }
-
-  public FilterRule getFilter(String anId) {
-    synchronized (filterRules) {
-      FilterRule result = (FilterRule) findFilter(filterRules, anId);
-      if (result == null)
-        return result;
-      else
-        return (FilterRule) result.clone();
-    }
-  }
-
-  public String setFilter(String anIdentifier, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {
-    return setFilter(filterRules, anIdentifier, aType, anExpression, aComments, aCommentAction, anArticleAction);
-  }
-
-  public void deleteFilter(String anIdentifier) {
-    deleteFilter(filterRules, anIdentifier);
-  }
-
-  public void moveFilterUp(String anIdentifier) {
-    moveFilter(filterRules, anIdentifier, -1);
-  }
-
-  public void moveFilterDown(String anIdentifier) {
-    moveFilter(filterRules, anIdentifier, 1);
-  }
-
-  private String addFilter(List aFilters, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction, Date aLastHit) {
-    MirAntiAbuseFilterType type = (MirAntiAbuseFilterType) filterTypes.get(aType);
-
-    if (type == null)
-      return "invalidtype";
-
-    if (!type.validate(anExpression)) {
-      return "invalidexpression";
-    }
-
-    FilterRule filter = new FilterRule();
-
-    filter.setId(generateId());
-    filter.setExpression(anExpression);
-    filter.setType(aType);
-    filter.setComments(aComments);
-    filter.setArticleAction(anArticleAction);
-    filter.setCommentAction(aCommentAction);
-    filter.setLastHit(aLastHit);
-
-    synchronized (aFilters) {
-      aFilters.add(filter);
-    }
-
-    return null;
-  }
-
-  private String setFilter(List aFilters, String anIdentifier, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {
-    MirAntiAbuseFilterType type = (MirAntiAbuseFilterType) filterTypes.get(aType);
-
-    if (type == null)
-      return "invalidtype";
-
-    if (!type.validate(anExpression)) {
-      return "invalidexpression";
-    }
-
-    synchronized (aFilters) {
-      FilterRule filter = findFilter(aFilters, anIdentifier);
-
-      if (filter != null) {
-        filter.setExpression(anExpression);
-        filter.setType(aType);
-        filter.setCommentAction(aCommentAction);
-        filter.setArticleAction(anArticleAction);
-        filter.setComments(aComments);
-      }
-
-      return null;
-    }
-  }
-
-  private FilterRule findFilter(List aFilters, String anIdentifier) {
-    synchronized (aFilters) {
-      Iterator i = aFilters.iterator();
-      while (i.hasNext()) {
-        FilterRule filter = (FilterRule) i.next();
-
-        if (filter.getId().equals(anIdentifier)) {
-          return filter;
-        }
-      }
-    }
-
-    return null;
-  }
-
-  private void moveFilter(List aFilters, String anIdentifier, int aDirection) {
-    synchronized (aFilters) {
-      for (int i = 0; i < aFilters.size(); i++) {
-        FilterRule rule = (FilterRule) aFilters.get(i);
-
-        if (rule.getId().equals(anIdentifier) && (i + aDirection >= 0) && (i + aDirection < aFilters.size())) {
-          aFilters.remove(rule);
-          aFilters.add(i + aDirection, rule);
-          break;
-        }
-      }
-    }
-  }
-
-  private void deleteFilter(List aFilters, String anIdentifier) {
-    synchronized (aFilters) {
-      FilterRule filter = findFilter(aFilters, anIdentifier);
-
-      if (filter != null) {
-        aFilters.remove(filter);
-      }
-    }
-  }
-
-  private String generateId() {
-    synchronized (this) {
-      maxIdentifier = maxIdentifier + 1;
-
-      return Integer.toString(maxIdentifier);
-    }
-  }
-
-  public class FilterRule {
-    private String identifier;
-    private String expression;
-    private String type;
-    private String comments;
-    private String articleAction;
-    private String commentAction;
-    private Date lastHit;
-
-    public FilterRule() {
-      expression = "";
-      type = "";
-      identifier = "";
-      comments = "";
-      articleAction = articleBlockAction;
-      commentAction = commentBlockAction;
-      lastHit = null;
-    }
-
-    public Date getLastHit() {
-      return lastHit;
-    }
-
-    public void setLastHit(Date aDate) {
-      lastHit = aDate;
-    }
-
-    public String getId() {
-      return identifier;
-    }
-
-    public void setId(String anId) {
-      identifier = anId;
-    }
-
-    public String getExpression() {
-      return expression;
-    }
-
-    public void setExpression(String anExpression) {
-      expression = anExpression;
-    }
-
-    public String getType() {
-      return type;
-    }
-
-    public void setType(String aType) {
-      type = aType;
-    }
-
-    public void setComments(String aComments) {
-      comments = aComments;
-    }
-
-    public String getComments() {
-      return comments;
-    }
-
-    public String getArticleAction() {
-      return articleAction;
-    }
-
-    public void setArticleAction(String anArticleAction) {
-      articleAction = anArticleAction;
-    }
-
-    public String getCommentAction() {
-      return commentAction;
-    }
-
-    public void setCommentAction(String aCommentAction) {
-      commentAction = aCommentAction;
-    }
-
-    public boolean test(Entity anEntity, Request aRequest) {
-      MirAntiAbuseFilterType filterType = (MirAntiAbuseFilterType) filterTypes.get(type);
-      try {
-        if (filterType != null)
-          return filterType.test(expression, anEntity, aRequest);
-      }
-      catch (Throwable t) {
-        logger.error("error while testing " + type + "-filter '" + expression + "'");
-      }
-
-      return false;
-    };
-
-    public Object clone() {
-      FilterRule result = new FilterRule();
-      result.setComments(getComments());
-      result.setExpression(getExpression());
-      result.setId(getId());
-      result.setType(getType());
-      result.setArticleAction(getArticleAction());
-      result.setCommentAction(getCommentAction());
-      result.setLastHit(getLastHit());
-
-      return result;
-    }
+  private String escapeConfigListEntry(String aFilterPart) {
+    return StringRoutines.replaceStringCharacters(aFilterPart,
+        new char[] {'\\', ':'},
+        new String[] {"\\\\", "\\:"});
   }
 
   private String escapeFilterPart(String aFilterPart) {
     return StringRoutines.replaceStringCharacters(aFilterPart,
-        new char[] {'\\', ':', '\n', '\r', '\t', ' '},
-        new String[] {"\\\\", "\\:", "\\n", "\\r", "\\t", "\\ "});
+        new char[] {'\\', '\n', '\r', '\t', ' '},
+        new String[] {"\\\\", "\\n", "\\r", "\\t", "\\ "});
   }
 
   private String deescapeFilterPart(String aFilterPart) {
@@ -745,92 +465,25 @@ public class Abuse {
         new String[] {"\\", ":", "\n", "\r", "\t", " "});
   }
 
-  private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {
-    synchronized (aFilters) {
-      Iterator i = aFilters.iterator();
-
-      while (i.hasNext()) {
-        FilterRule filter = (FilterRule) i.next();
-
-        String filterconfig =
-            escapeFilterPart(filter.getType()) + ":" +
-            escapeFilterPart(filter.getExpression()) + ":" +
-            escapeFilterPart(filter.getArticleAction()) + ":" +
-            escapeFilterPart(filter.getCommentAction()) + ":" +
-            escapeFilterPart(filter.getComments()) + ":";
-
-        if (filter.getLastHit() != null)
-          filterconfig = filterconfig + filter.getLastHit().getTime();
-
-        aConfiguration.addProperty(aConfigKey, filterconfig);
-      }
-    }
-  }
-
-  private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {
-    synchronized (aFilters) {
-      aFilters.clear();
-
-      if (aConfiguration.getStringArray(aConfigKey) != null) {
-
-        Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).
-            iterator();
-
-        while (i.hasNext()) {
-          String filter = (String) i.next();
-          List parts = StringRoutines.splitStringWithEscape(filter, ':', '\\');
-          if (parts.size() == 2) {
-            parts.add(articleBlockAction);
-            parts.add(commentBlockAction);
-            parts.add("");
-            parts.add("");
-          }
-
-          if (parts.size() >= 5) {
-            Date lastHit = null;
-
-            if (parts.size() >= 6) {
-              String lastHitString = (String) parts.get(5);
-
-              try {
-                lastHit = new Date(Long.parseLong(lastHitString));
-              }
-              catch (Throwable t) {
-              }
-            }
-
-            addFilter(deescapeFilterPart( (String) parts.get(0)),
-                      deescapeFilterPart( (String) parts.get(1)),
-                      deescapeFilterPart( (String) parts.get(4)),
-                      deescapeFilterPart( (String) parts.get(3)),
-                      deescapeFilterPart( (String) parts.get(2)), lastHit);
-          }
-        }
-      }
-    }
-  }
-
   private static class LogEntry {
     private String ipNumber;
     private String browserString;
     private String id;
     private Date timeStamp;
     private boolean isArticle;
-    private String hitFilterType;
-    private String hitFilterExpression;
+    private String matchingFilterTag;
 
-    public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle, String aHitFilterType, String aHitFilterExpression) {
+    public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle, String aMatchingFilterTag) {
       ipNumber = anIpNumber;
       browserString = aBrowserString;
       id = anId;
       isArticle = anIsArticle;
       timeStamp = aTimeStamp;
-      hitFilterType = aHitFilterType;
-      hitFilterExpression = aHitFilterExpression;
+      matchingFilterTag = aMatchingFilterTag;
     }
 
     public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {
-      this(aTimeStamp, anIpNumber, aBrowserString, anId, anIsArticle, null, null);
+      this(aTimeStamp, anIpNumber, aBrowserString, anId, anIsArticle, null);
     }
 
     public String getIpNumber() {
@@ -845,12 +498,8 @@ public class Abuse {
       return id;
     }
 
-    public String getHitFilterType() {
-      return hitFilterType;
-    }
-
-    public String getHitFilterExpression() {
-      return hitFilterExpression;
+    public String getMatchingFilterTag() {
+      return matchingFilterTag;
     }
 
     public Date getTimeStamp() {
@@ -868,16 +517,16 @@ public class Abuse {
         log.clear();
       else {
         while (log.size() > 0 && log.size() > logSize) {
-          log.remove(0);
+          log.remove(log.size()-1);
         }
       }
     }
-  };
+  }
 
   private void appendLog(LogEntry anEntry) {
     synchronized (log) {
       if (logEnabled) {
-        log.add(anEntry);
+        log.add(0, anEntry);
         truncateLog();
       }
     }