tiny fixes here and there
[mir.git] / source / mircoders / global / Abuse.java
index 1b54afe..dd54988 100755 (executable)
-/*\r
- * Copyright (C) 2001, 2002 The Mir-coders group\r
- *\r
- * This file is part of Mir.\r
- *\r
- * Mir is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * Mir is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with Mir; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
- *\r
- * In addition, as a special exception, The Mir-coders gives permission to link\r
- * the code of this program with  any library licensed under the Apache Software License,\r
- * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library\r
- * (or with modified versions of the above that use the same license as the above),\r
- * and distribute linked combinations including the two.  You must obey the\r
- * GNU General Public License in all respects for all of the code used other than\r
- * the above mentioned libraries.  If you modify this file, you may extend this\r
- * exception to your version of the file, but you are not obligated to do so.\r
- * If you do not wish to do so, delete this exception statement from your version.\r
- */\r
-\r
-package mircoders.global;\r
-\r
-import java.io.File;\r
-import java.io.FileNotFoundException;\r
-import java.io.FileOutputStream;\r
-import java.util.Arrays;\r
-import java.util.Date;\r
-import java.util.GregorianCalendar;\r
-import java.util.HashMap;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Random;\r
-import java.util.Vector;\r
-import javax.servlet.http.Cookie;\r
-import javax.servlet.http.HttpServletResponse;\r
-\r
-import org.apache.commons.collections.ExtendedProperties;\r
-import mir.config.MirPropertiesConfiguration;\r
-import mir.entity.Entity;\r
-import mir.log.LoggerWrapper;\r
-import mir.session.Request;\r
-import mir.util.GeneratorFormatAdapters;\r
-import mir.util.StringRoutines;\r
-import mircoders.entity.EntityComment;\r
-import mircoders.entity.EntityContent;\r
-import mircoders.entity.EntityUsers;\r
-import mircoders.localizer.MirAdminInterfaceLocalizer;\r
-import mircoders.localizer.MirAntiAbuseFilterType;\r
-\r
-\r
-public class Abuse {\r
-  private List filterRules;\r
-  private Map filterTypes;\r
-  private List filterTypeIds;\r
-  private int maxIdentifier;\r
-  private LoggerWrapper logger;\r
-  private LoggerWrapper adminUsageLogger;\r
-  private int logSize;\r
-  private boolean logEnabled;\r
-  private boolean openPostingDisabled;\r
-  private boolean openPostingPassword;\r
-  private boolean cookieOnBlock;\r
-  private String articleBlockAction;\r
-  private String commentBlockAction;\r
-  private List log;\r
-  private String configFile = MirGlobal.config().getStringWithHome("Abuse.Config");\r
-\r
-  private MirPropertiesConfiguration configuration;\r
-\r
-  private static String cookieName=MirGlobal.config().getString("Abuse.CookieName");\r
-  private static int cookieMaxAge = 60*60*MirGlobal.config().getInt("Abuse.CookieMaxAge");\r
-\r
-  public Abuse() {\r
-    logger = new LoggerWrapper("Global.Abuse");\r
-    adminUsageLogger = new LoggerWrapper("AdminUsage");\r
-    filterRules = new Vector();\r
-    maxIdentifier = 0;\r
-    log = new Vector();\r
-\r
-    try {\r
-      configuration = MirPropertiesConfiguration.instance();\r
-    }\r
-    catch (Throwable e) {\r
-      throw new RuntimeException("Can't get configuration: " + e.getMessage());\r
-    }\r
-\r
-    logSize = 100;\r
-    logEnabled = false;\r
-    articleBlockAction = "";\r
-    commentBlockAction = "";\r
-    openPostingPassword = false;\r
-    openPostingDisabled = false;\r
-    cookieOnBlock = false;\r
-\r
-    try {\r
-      filterTypes = new HashMap();\r
-      filterTypeIds = new Vector();\r
-\r
-      Iterator i = MirGlobal.localizer().openPostings().getAntiAbuseFilterTypes().iterator();\r
-\r
-      while (i.hasNext()) {\r
-        MirAntiAbuseFilterType filterType = (MirAntiAbuseFilterType) i.next();\r
-        filterTypes.put(filterType.getName(), filterType);\r
-        filterTypeIds.add(filterType.getName());\r
-      }\r
-    }\r
-    catch (Throwable t) {\r
-      throw new RuntimeException("Can't get filter types: " + t.getMessage());\r
-    }\r
-\r
-    load();\r
-  }\r
-\r
-  private void setCookie(HttpServletResponse aResponse) {\r
-    Random random = new Random();\r
-\r
-    Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000)));\r
-    cookie.setMaxAge(cookieMaxAge);\r
-    cookie.setPath("/");\r
-\r
-    if (aResponse!=null)\r
-      aResponse.addCookie(cookie);\r
-  }\r
-\r
-  private boolean checkCookie(List aCookies) {\r
-    if (getCookieOnBlock()) {\r
-      Iterator i = aCookies.iterator();\r
-\r
-      while (i.hasNext()) {\r
-        Cookie cookie = (Cookie) i.next();\r
-\r
-        if (cookie.getName().equals(cookieName)) {\r
-          logger.debug("cookie match");\r
-          return true;\r
-        }\r
-      }\r
-    }\r
-\r
-    return false;\r
-  }\r
-  FilterRule findMatchingFilter(Entity anEntity, Request aRequest) {\r
-    Iterator iterator = filterRules.iterator();\r
-\r
-    while (iterator.hasNext()) {\r
-      FilterRule rule = (FilterRule) iterator.next();\r
-\r
-      if (rule.test(anEntity, aRequest))\r
-        return rule;\r
-    }\r
-\r
-    return null;\r
-  }\r
-\r
-  public void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) {\r
-    try {\r
-      long time = System.currentTimeMillis();\r
-\r
-      FilterRule filterRule = findMatchingFilter(aComment, aRequest);\r
-\r
-      if (filterRule!=null) {\r
-        logger.debug("Match for " + filterRule.getType()+" rule '"+ filterRule.getExpression()+"'");\r
-        filterRule.setLastHit(new GregorianCalendar().getTime());\r
-        MirGlobal.performCommentOperation(null, aComment, filterRule.getCommentAction());\r
-        setCookie(aResponse);\r
-      }\r
-\r
-      logger.debug("checkComment: " + (System.currentTimeMillis()-time) + "ms");\r
-    }\r
-    catch (Throwable t) {\r
-      t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));\r
-      logger.error("Abuse.checkComment: " + t.toString());\r
-    }\r
-  }\r
-\r
-  public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {\r
-    try {\r
-      long time = System.currentTimeMillis();\r
-\r
-      FilterRule filterRule = findMatchingFilter(anArticle, aRequest);\r
-\r
-      if (filterRule!=null) {\r
-        logger.debug("Match for " + filterRule.getType() + " rule '" + filterRule.getExpression()+"'");\r
-        filterRule.setLastHit(new GregorianCalendar().getTime());\r
-        MirGlobal.performArticleOperation(null, anArticle, filterRule.getArticleAction());\r
-        setCookie(aResponse);\r
-      }\r
-\r
-      logger.info("checkArticle: " + (System.currentTimeMillis()-time) + "ms");\r
-    }\r
-    catch (Throwable t) {\r
-      t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));\r
-      logger.error("Abuse.checkArticle: " + t.toString());\r
-    }\r
-  }\r
-\r
-  public boolean getLogEnabled() {\r
-    return logEnabled;\r
-  }\r
-\r
-  public void setLogEnabled(boolean anEnabled) {\r
-    if (!configuration.getString("Abuse.DisallowIPLogging", "0").equals("1"))\r
-      logEnabled = anEnabled;\r
-    truncateLog();\r
-  }\r
-\r
-  public int getLogSize() {\r
-    return logSize;\r
-  }\r
-\r
-  public void setLogSize(int aSize) {\r
-    logSize = aSize;\r
-    truncateLog();\r
-  }\r
-\r
-  public boolean getOpenPostingDisabled() {\r
-    return openPostingDisabled;\r
-  }\r
-\r
-  public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {\r
-    openPostingDisabled = anOpenPostingDisabled;\r
-  }\r
-\r
-  public boolean getOpenPostingPassword() {\r
-    return openPostingPassword;\r
-  }\r
-\r
-  public void setOpenPostingPassword(boolean anOpenPostingPassword) {\r
-    openPostingPassword = anOpenPostingPassword;\r
-  }\r
-\r
-  public boolean getCookieOnBlock() {\r
-    return cookieOnBlock;\r
-  }\r
-\r
-  public void setCookieOnBlock(boolean aCookieOnBlock) {\r
-    cookieOnBlock = aCookieOnBlock;\r
-  }\r
-\r
-  public String getArticleBlockAction() {\r
-    return articleBlockAction;\r
-  }\r
-\r
-  public void setArticleBlockAction(String anAction) {\r
-    articleBlockAction = anAction;\r
-  }\r
-\r
-  public String getCommentBlockAction() {\r
-    return commentBlockAction;\r
-  }\r
-\r
-  public void setCommentBlockAction(String anAction) {\r
-    commentBlockAction = anAction;\r
-  }\r
-\r
-  public List getLog() {\r
-    synchronized(log) {\r
-      try {\r
-        List result = new Vector();\r
-\r
-        Iterator i = log.iterator();\r
-        while (i.hasNext()) {\r
-          LogEntry logEntry = (LogEntry) i.next();\r
-          Map entry = new HashMap();\r
-\r
-          entry.put("ip", logEntry.getIpNumber());\r
-          entry.put("id", logEntry.getId());\r
-          entry.put("timestamp", new GeneratorFormatAdapters.DateFormatAdapter(logEntry.getTimeStamp(), MirPropertiesConfiguration.instance().getString("Mir.DefaultTimezone")));\r
-          if (logEntry.getIsArticle())\r
-            entry.put("type", "content");\r
-          else\r
-            entry.put("type", "comment");\r
-          entry.put("browser", logEntry.getBrowserString());\r
-\r
-          result.add(entry);\r
-        }\r
-\r
-        return result;\r
-      }\r
-      catch (Throwable t) {\r
-        throw new RuntimeException(t.toString());\r
-      }\r
-    }\r
-  }\r
-\r
-  public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser) {\r
-    appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false));\r
-  }\r
-\r
-  public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser) {\r
-    appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true));\r
-  }\r
-\r
-  public void load() {\r
-    try {\r
-      ExtendedProperties configuration = new ExtendedProperties();\r
-\r
-      try {\r
-        configuration = new ExtendedProperties(configFile);\r
-      }\r
-      catch (FileNotFoundException e) {\r
-      }\r
-\r
-      getFilterConfig(filterRules, "abuse.filter", configuration);\r
-\r
-      setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));\r
-      setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));\r
-      setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));\r
-      setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));\r
-      setLogSize(configuration.getInt("abuse.logSize", 10));\r
-      setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));\r
-      setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));\r
-    }\r
-    catch (Throwable t) {\r
-      throw new RuntimeException(t.toString());\r
-    }\r
-  }\r
-  public void save() {\r
-    try {\r
-      ExtendedProperties configuration = new ExtendedProperties();\r
-\r
-      setFilterConfig(filterRules, "abuse.filter", configuration);\r
-\r
-      configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled()?"1":"0");\r
-      configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword()?"1":"0");\r
-      configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock()?"1":"0");\r
-      configuration.addProperty("abuse.logEnabled", getLogEnabled()?"1":"0");\r
-      configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));\r
-      configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());\r
-      configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());\r
-\r
-      configuration.save(new FileOutputStream(new File(configFile)), "Anti abuse configuration");\r
-    }\r
-    catch (Throwable t) {\r
-      throw new RuntimeException(t.toString());\r
-    }\r
-  }\r
-\r
-  public List getFilterTypes() {\r
-    try {\r
-      List result = new Vector();\r
-\r
-      Iterator i = filterTypeIds.iterator();\r
-      while (i.hasNext()) {\r
-        String id = (String) i.next();\r
-\r
-        Map action = new HashMap();\r
-        action.put("resource", id);\r
-        action.put("identifier", id);\r
-\r
-        result.add(action);\r
-      }\r
-\r
-      return result;\r
-    }\r
-    catch (Throwable t) {\r
-      throw new RuntimeException("can't get article actions");\r
-    }\r
-  }\r
-\r
-  public List getArticleActions() {\r
-    try {\r
-      List result = new Vector();\r
-\r
-      Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();\r
-      while (i.hasNext()) {\r
-        MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =\r
-            (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();\r
-\r
-        Map action = new HashMap();\r
-        action.put("resource", operation.getName());\r
-        action.put("identifier", operation.getName());\r
-\r
-        result.add(action);\r
-      }\r
-\r
-      return result;\r
-    }\r
-    catch (Throwable t) {\r
-      throw new RuntimeException("can't get article actions");\r
-    }\r
-  }\r
-\r
-  public List getCommentActions() {\r
-    try {\r
-      List result = new Vector();\r
-\r
-      Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();\r
-      while (i.hasNext()) {\r
-        MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =\r
-            (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();\r
-\r
-        Map action = new HashMap();\r
-        action.put("resource", operation.getName());\r
-        action.put("identifier", operation.getName());\r
-\r
-        result.add(action);\r
-      }\r
-\r
-      return result;\r
-    }\r
-    catch (Throwable t) {\r
-      throw new RuntimeException("can't get comment actions");\r
-    }\r
-  }\r
-\r
-  public List getFilters() {\r
-    List result = new Vector();\r
-\r
-    synchronized(filterRules) {\r
-      Iterator i = filterRules.iterator();\r
-      while (i.hasNext()) {\r
-        FilterRule filter = (FilterRule) i.next();\r
-        result.add(filter.clone());\r
-      }\r
-      return result;\r
-    }\r
-  }\r
-\r
-  public String addFilter(String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {\r
-    return addFilter(filterRules, aType, anExpression, aComments, aCommentAction, anArticleAction);\r
-  }\r
-\r
-  public FilterRule getFilter(String anId) {\r
-    synchronized (filterRules) {\r
-      FilterRule result = (FilterRule) findFilter(filterRules, anId);\r
-      if (result==null)\r
-        return result;\r
-      else\r
-        return (FilterRule) result.clone();\r
-    }\r
-  }\r
-\r
-  public String setFilter(String anIdentifier, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {\r
-    return setFilter(filterRules, anIdentifier, aType, anExpression, aComments, aCommentAction, anArticleAction);\r
-  }\r
-\r
-  public void deleteFilter(String anIdentifier) {\r
-    deleteFilter(filterRules, anIdentifier);\r
-  }\r
-\r
-  private String addFilter(List aFilters, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {\r
-    MirAntiAbuseFilterType type = (MirAntiAbuseFilterType) filterTypes.get(aType);\r
-\r
-    if (type==null)\r
-      return "invalidtype";\r
-\r
-    if (!type.validate(anExpression)) {\r
-      return "invalidexpression";\r
-    }\r
-\r
-    FilterRule filter = new FilterRule();\r
-\r
-    filter.setId(generateId());\r
-    filter.setExpression(anExpression);\r
-    filter.setType(aType);\r
-    filter.setComments(aComments);\r
-    filter.setArticleAction(anArticleAction);\r
-    filter.setCommentAction(aCommentAction);\r
-\r
-    synchronized (aFilters) {\r
-      aFilters.add(filter);\r
-    }\r
-\r
-    return null;\r
-  }\r
-\r
-  private String setFilter(List aFilters, String anIdentifier, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {\r
-    MirAntiAbuseFilterType type = (MirAntiAbuseFilterType) filterTypes.get(aType);\r
-\r
-    if (type==null)\r
-      return "invalidtype";\r
-\r
-    if (!type.validate(anExpression)) {\r
-      return "invalidexpression";\r
-    }\r
-\r
-    synchronized (aFilters) {\r
-      FilterRule filter = findFilter(aFilters, anIdentifier);\r
-\r
-      if (filter!=null) {\r
-        filter.setExpression(anExpression);\r
-        filter.setType(aType);\r
-        filter.setCommentAction(aCommentAction);\r
-        filter.setArticleAction(anArticleAction);\r
-        filter.setComments(aComments);\r
-      }\r
-\r
-      return null;\r
-    }\r
-  }\r
-\r
-  private FilterRule findFilter(List aFilters, String anIdentifier) {\r
-    synchronized (aFilters) {\r
-      Iterator i = aFilters.iterator();\r
-      while (i.hasNext()) {\r
-        FilterRule filter = (FilterRule) i.next();\r
-\r
-        if (filter.getId().equals(anIdentifier)) {\r
-          return filter;\r
-        }\r
-      }\r
-    }\r
-\r
-    return null;\r
-  }\r
-\r
-  private void deleteFilter(List aFilters, String anIdentifier) {\r
-    synchronized (aFilters) {\r
-      FilterRule filter = findFilter(aFilters, anIdentifier);\r
-\r
-      if (filter!=null) {\r
-        aFilters.remove(filter);\r
-      }\r
-    }\r
-  }\r
-\r
-  private String generateId() {\r
-    synchronized(this) {\r
-      maxIdentifier = maxIdentifier+1;\r
-\r
-      return Integer.toString(maxIdentifier);\r
-    }\r
-  }\r
-\r
-  public class FilterRule {\r
-    private String identifier;\r
-    private String expression;\r
-    private String type;\r
-    private String comments;\r
-    private String articleAction;\r
-    private String commentAction;\r
-    private Date lastHit;\r
-\r
-    public FilterRule() {\r
-      expression = "";\r
-      type = "";\r
-      identifier = "";\r
-      comments = "";\r
-      articleAction = articleBlockAction;\r
-      commentAction = commentBlockAction;\r
-      lastHit = null;\r
-    }\r
-\r
-    public Date getLastHit() {\r
-      return lastHit;\r
-    }\r
-\r
-    public void setLastHit(Date aDate) {\r
-      lastHit = aDate;\r
-    }\r
-\r
-    public String getId() {\r
-      return identifier;\r
-    }\r
-\r
-    public void setId(String anId) {\r
-      identifier = anId;\r
-    }\r
-\r
-    public String getExpression() {\r
-      return expression;\r
-    }\r
-\r
-    public void setExpression(String anExpression) {\r
-      expression = anExpression;\r
-    }\r
-\r
-    public String getType() {\r
-      return type;\r
-    }\r
-\r
-    public void setType(String aType) {\r
-      type = aType;\r
-    }\r
-\r
-    public void setComments(String aComments) {\r
-      comments = aComments;\r
-    }\r
-\r
-    public String getComments() {\r
-      return comments;\r
-    }\r
-\r
-    public String getArticleAction() {\r
-      return articleAction;\r
-    }\r
-\r
-    public void setArticleAction(String anArticleAction) {\r
-      articleAction = anArticleAction;\r
-    }\r
-\r
-    public String getCommentAction() {\r
-      return commentAction;\r
-    }\r
-\r
-    public void setCommentAction(String aCommentAction) {\r
-      commentAction = aCommentAction;\r
-    }\r
-\r
-    public boolean test(Entity anEntity, Request aRequest) {\r
-      MirAntiAbuseFilterType filterType = (MirAntiAbuseFilterType) filterTypes.get(type);\r
-      try {\r
-        if (filterType != null)\r
-          return filterType.test(expression, anEntity, aRequest);\r
-      }\r
-      catch (Throwable t) {\r
-        logger.error("error while testing "+type+"-filter '"+expression+"'");\r
-      }\r
-\r
-      return false;\r
-    };\r
-\r
-    public Object clone() {\r
-      FilterRule result = new FilterRule();\r
-      result.setComments(getComments());\r
-      result.setExpression(getExpression());\r
-      result.setId(getId());\r
-      result.setType(getType());\r
-      result.setArticleAction(getArticleAction());\r
-      result.setCommentAction(getCommentAction());\r
-      result.setLastHit(getLastHit());\r
-\r
-      return result;\r
-    }\r
-  }\r
-\r
-  private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {\r
-    synchronized(aFilters) {\r
-      Iterator i = aFilters.iterator();\r
-\r
-      while (i.hasNext()) {\r
-        FilterRule filter = (FilterRule) i.next();\r
-\r
-        String comments = StringRoutines.replaceStringCharacters(filter.getComments(), new char[] { '\\', ':'}, new String[] { "\\\\", "\\:"} );\r
-\r
-        aConfiguration.addProperty(aConfigKey, filter.getType()+":"+filter.getExpression()+":"+filter.getArticleAction() + ":" + filter.getCommentAction() + ":" +\r
-           comments);\r
-      }\r
-    }\r
-  }\r
-\r
-  private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {\r
-    synchronized(aFilters) {\r
-      aFilters.clear();\r
-\r
-      if (aConfiguration.getStringArray(aConfigKey)!=null) {\r
-\r
-        Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).\r
-            iterator();\r
-\r
-        while (i.hasNext()) {\r
-          String filter = (String) i.next();\r
-          List parts = StringRoutines.splitStringWithEscape(filter, ':', '\\');\r
-          if (parts.size() == 2) {\r
-            parts.add(articleBlockAction);\r
-            parts.add(commentBlockAction);\r
-            parts.add("");\r
-          }\r
-\r
-          if (parts.size() == 5) {\r
-            addFilter( (String) parts.get(0), (String) parts.get(1), (String) parts.get(4), (String) parts.get(3), (String) parts.get(2));\r
-          }\r
-        }\r
-      }\r
-    }\r
-  }\r
-\r
-  private static class LogEntry {\r
-    private String ipNumber;\r
-    private String browserString;\r
-    private String id;\r
-    private Date timeStamp;\r
-    private boolean isArticle;\r
-\r
-    public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {\r
-      ipNumber = anIpNumber;\r
-      browserString = aBrowserString;\r
-      id = anId;\r
-      isArticle = anIsArticle;\r
-      timeStamp=aTimeStamp;\r
-    }\r
-\r
-    public String getIpNumber() {\r
-      return ipNumber;\r
-    }\r
-\r
-    public String getBrowserString() {\r
-      return browserString;\r
-    }\r
-\r
-    public String getId() {\r
-      return id;\r
-    }\r
-\r
-    public Date getTimeStamp() {\r
-      return timeStamp;\r
-    }\r
-\r
-    public boolean getIsArticle() {\r
-      return isArticle;\r
-    }\r
-  }\r
-\r
-  private void truncateLog() {\r
-    synchronized(log) {\r
-      if (!logEnabled)\r
-        log.clear();\r
-      else {\r
-        while (log.size()>0 && log.size()>logSize) {\r
-          log.remove(0);\r
-        }\r
-      }\r
-    }\r
-  };\r
-\r
-  private void appendLog(LogEntry anEntry) {\r
-    synchronized (log) {\r
-      if (logEnabled) {\r
-        log.add(anEntry);\r
-        truncateLog();\r
-      }\r
-    }\r
-  }\r
-\r
-  public void logAdminUsage(EntityUsers aUser, String aDescription) {\r
-    try {\r
-      String user = "unknown (" + aUser.toString() +")";\r
-      if (user!=null)\r
-        user = aUser.getValue("login");\r
-      adminUsageLogger.info(user + ": " + aDescription);\r
-    }\r
-    catch (Throwable t) {\r
-      logger.error("Error while logging admin usage ("+aUser.toString()+", "+aDescription+"): " +t.toString());\r
-    }\r
-  }\r
-}
\ No newline at end of file
+/*
+ * Copyright (C) 2001, 2002 The Mir-coders group
+ *
+ * This file is part of Mir.
+ *
+ * Mir is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Mir is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mir; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * In addition, as a special exception, The Mir-coders gives permission to link
+ * the code of this program with  any library licensed under the Apache Software License,
+ * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
+ * (or with modified versions of the above that use the same license as the above),
+ * and distribute linked combinations including the two.  You must obey the
+ * GNU General Public License in all respects for all of the code used other than
+ * the above mentioned libraries.  If you modify this file, you may extend this
+ * exception to your version of the file, but you are not obligated to do so.
+ * If you do not wish to do so, delete this exception statement from your version.
+ */
+
+package mircoders.global;
+
+import mir.config.MirPropertiesConfiguration;
+import mir.entity.Entity;
+import mir.entity.adapter.EntityAdapterModel;
+import mir.log.LoggerWrapper;
+import mir.session.Request;
+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.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 LoggerWrapper logger;
+  private int logSize;
+  private boolean logEnabled;
+  private boolean openPostingDisabled;
+  private boolean openPostingPassword;
+  private boolean cookieOnBlock;
+  private String articleBlockAction;
+  private String commentBlockAction;
+  private List log;
+  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(EntityAdapterModel aModel) {
+    logger = new LoggerWrapper("Global.Abuse");
+    filterEngine = new FilterEngine(aModel);
+    model = aModel;
+
+    log = new ArrayList();
+
+    try {
+      configuration = MirPropertiesConfiguration.instance();
+    }
+    catch (Throwable e) {
+      throw new RuntimeException("Can't get configuration: " + e.getMessage());
+    }
+
+    logSize = 100;
+    logEnabled = false;
+    articleBlockAction = "";
+    commentBlockAction = "";
+    openPostingPassword = false;
+    openPostingDisabled = false;
+    cookieOnBlock = false;
+
+    load();
+  }
+
+  public FilterEngine getFilterEngine() {
+    return filterEngine;
+  }
+
+  private void setCookie(HttpServletResponse aResponse) {
+    Random random = new Random();
+
+    Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000)));
+    cookie.setMaxAge(cookieMaxAge);
+    cookie.setPath("/");
+
+    if (aResponse != null)
+      aResponse.addCookie(cookie);
+  }
+
+  private boolean checkCookie(List aCookies) {
+    if (getCookieOnBlock()) {
+      Iterator i = aCookies.iterator();
+
+      while (i.hasNext()) {
+        Cookie cookie = (Cookie) i.next();
+
+        if (cookie.getName().equals(cookieName)) {
+          logger.debug("cookie match");
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+  /** 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();
+
+      FilterEngine.Filter matchingFilter = filterEngine.testPosting(aComment, aRequest);
+
+      if (matchingFilter != null) {
+        logger.debug("Match for " + matchingFilter.getTag());
+        matchingFilter.updateLastHit(new GregorianCalendar().getTime());
+
+        StringBuffer line = new StringBuffer();
+
+        line.append(DateTimeRoutines.advancedDateFormat(
+            configuration.getString("Mir.DefaultDateTimeFormat"),
+            (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));
+
+        line.append(" ");
+        line.append(matchingFilter.getTag());
+        EntityUtility.appendLineToField(aComment, "comment", line.toString());
+
+        MirGlobal.performCommentOperation(null, aComment, matchingFilter.getCommentAction());
+        setCookie(aResponse);
+        save();
+        logComment(aComment, aRequest, matchingFilter.getTag());
+      }
+      else {
+        logComment(aComment, aRequest);
+      }
+
+      logger.debug("checkComment: " + (System.currentTimeMillis() - time) + "ms");
+    }
+    catch (Throwable t) {
+      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();
+
+      FilterEngine.Filter matchingFilter = filterEngine.testPosting(anArticle, aRequest);
+
+      if (matchingFilter != null) {
+        logger.debug("Match for " + matchingFilter.getTag());
+//        matchingFilter.updateLastHit(new GregorianCalendar().getTime());
+
+        StringBuffer line = new StringBuffer();
+
+        line.append(DateTimeRoutines.advancedDateFormat(
+            configuration.getString("Mir.DefaultDateTimeFormat"),
+            (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));
+
+        line.append(" ");
+        line.append(matchingFilter.getTag());
+        EntityUtility.appendLineToField(anArticle, "comment", line.toString());
+
+        MirGlobal.performArticleOperation(null, anArticle, matchingFilter.getArticleAction());
+        setCookie(aResponse);
+        save();
+        logArticle(anArticle, aRequest, matchingFilter.getTag());
+      }
+      else {
+        logArticle(anArticle, aRequest);
+      }
+
+      logger.info("checkArticle: " + (System.currentTimeMillis() - time) + "ms");
+    }
+    catch (Throwable t) {
+      logger.error("Exception thrown while checking article", t);
+    }
+  }
+
+  public boolean getLogEnabled() {
+    return logEnabled;
+  }
+
+  public void setLogEnabled(boolean anEnabled) {
+    if (!configuration.getString("Abuse.DisallowIPLogging", "0").equals("1"))
+      logEnabled = anEnabled;
+    truncateLog();
+  }
+
+  public int getLogSize() {
+    return logSize;
+  }
+
+  public void setLogSize(int aSize) {
+    logSize = aSize;
+    truncateLog();
+  }
+
+  public boolean getOpenPostingDisabled() {
+    return openPostingDisabled;
+  }
+
+  public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {
+    openPostingDisabled = anOpenPostingDisabled;
+  }
+
+  public boolean getOpenPostingPassword() {
+    return openPostingPassword;
+  }
+
+  public void setOpenPostingPassword(boolean anOpenPostingPassword) {
+    openPostingPassword = anOpenPostingPassword;
+  }
+
+  public boolean getCookieOnBlock() {
+    return cookieOnBlock;
+  }
+
+  public void setCookieOnBlock(boolean aCookieOnBlock) {
+    cookieOnBlock = aCookieOnBlock;
+  }
+
+  public String getArticleBlockAction() {
+    return articleBlockAction;
+  }
+
+  public void setArticleBlockAction(String anAction) {
+    articleBlockAction = anAction;
+  }
+
+  public String getCommentBlockAction() {
+    return commentBlockAction;
+  }
+
+  public void setCommentBlockAction(String anAction) {
+    commentBlockAction = anAction;
+  }
+
+  public List getLog() {
+    ModuleContent contentModule = new ModuleContent();
+    ModuleComment commentModule = new ModuleComment();
+
+    synchronized (log) {
+      try {
+        List result = new ArrayList();
+
+        Iterator i = log.iterator();
+        while (i.hasNext()) {
+          LogEntry logEntry = (LogEntry) i.next();
+          Map entry = new HashMap();
+
+          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()) {
+            entry.put("type", "content");
+            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("filtertag", logEntry.getMatchingFilterTag());
+
+          result.add(entry);
+        }
+
+        return result;
+      }
+      catch (Throwable t) {
+        throw new RuntimeException(t.toString());
+      }
+    }
+  }
+
+  public void logComment(Entity aComment, Request aRequest) {
+    logComment(aComment, aRequest, null);
+  }
+
+  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, aMatchingFilterTag);
+  }
+
+  public void logArticle(Entity anArticle, Request aRequest) {
+    logArticle(anArticle, aRequest, null);
+  }
+
+  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, aMatchingFilterTag);
+  }
+
+  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 aMatchingFilterTag) {
+    appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true, aMatchingFilterTag));
+  }
+
+  public synchronized void load() {
+    try {
+      ExtendedProperties configuration = new ExtendedProperties();
+
+      try {
+        configuration = new ExtendedProperties(configFile.getAbsolutePath());
+      }
+      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 synchronized void save() {
+    try {
+      ExtendedProperties configuration = new ExtendedProperties();
+
+      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(configFile),8192), "Anti abuse configuration");
+    }
+    catch (Throwable t) {
+      throw new RuntimeException(t.toString());
+    }
+  }
+
+  public List getArticleActions() {
+    try {
+      List result = new ArrayList();
+
+      Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();
+      while (i.hasNext()) {
+        MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
+            (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
+
+        Map action = new HashMap();
+        action.put("resource", operation.getName());
+        action.put("identifier", operation.getName());
+
+        result.add(action);
+      }
+
+      return result;
+    }
+    catch (Throwable t) {
+      throw new RuntimeException("can't get article actions");
+    }
+  }
+
+  public List getCommentActions() {
+    try {
+      List result = new ArrayList();
+
+      Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();
+      while (i.hasNext()) {
+        MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
+            (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
+
+        Map action = new HashMap();
+        action.put("resource", operation.getName());
+        action.put("identifier", operation.getName());
+
+        result.add(action);
+      }
+
+      return result;
+    }
+    catch (Throwable t) {
+      throw new RuntimeException("can't get comment actions");
+    }
+  }
+
+  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", "\\ "});
+  }
+
+  private String deescapeFilterPart(String aFilterPart) {
+    return StringRoutines.replaceEscapedStringCharacters(aFilterPart,
+        '\\',
+        new char[] {'\\', ':', 'n', 'r', 't', ' '},
+        new String[] {"\\", ":", "\n", "\r", "\t", " "});
+  }
+
+  private static class LogEntry {
+    private String ipNumber;
+    private String browserString;
+    private String id;
+    private Date timeStamp;
+    private boolean isArticle;
+    private String matchingFilterTag;
+
+    public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle, String aMatchingFilterTag) {
+      ipNumber = anIpNumber;
+      browserString = aBrowserString;
+      id = anId;
+      isArticle = anIsArticle;
+      timeStamp = aTimeStamp;
+      matchingFilterTag = aMatchingFilterTag;
+    }
+
+    public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {
+      this(aTimeStamp, anIpNumber, aBrowserString, anId, anIsArticle, null);
+    }
+
+    public String getIpNumber() {
+      return ipNumber;
+    }
+
+    public String getBrowserString() {
+      return browserString;
+    }
+
+    public String getId() {
+      return id;
+    }
+
+    public String getMatchingFilterTag() {
+      return matchingFilterTag;
+    }
+
+    public Date getTimeStamp() {
+      return timeStamp;
+    }
+
+    public boolean getIsArticle() {
+      return isArticle;
+    }
+  }
+
+  private void truncateLog() {
+    synchronized (log) {
+      if (!logEnabled)
+        log.clear();
+      else {
+        while (log.size() > 0 && log.size() > logSize) {
+          log.remove(log.size()-1);
+        }
+      }
+    }
+  }
+
+  private void appendLog(LogEntry anEntry) {
+    synchronized (log) {
+      if (logEnabled) {
+        log.add(0, anEntry);
+        truncateLog();
+      }
+    }
+  }
+}