--- /dev/null
+/*\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
+package mircoders.localizer.basic.filters;\r
+\r
+import mircoders.localizer.basic.MirBasicAntiAbuseFilterTypes;\r
+import mircoders.entity.EntityComment;\r
+\r
+import java.util.*;\r
+\r
+import mir.util.StringRoutines;\r
+import mir.entity.Entity;\r
+import mir.session.Request;\r
+\r
+/**\r
+ * A ip-based throttling filter.\r
+ *\r
+ * <p>\r
+ * Expressions have the form <time in minutes>:<posting limit>\r
+ */\r
+public class ThrottleFilter extends MirBasicAntiAbuseFilterTypes.BasicFilterType {\r
+ private long overallHorizon;\r
+\r
+ private ThrottleManager throttleManager;\r
+\r
+ public ThrottleFilter(String aName, long anOverallHorizon) {\r
+ super(aName);\r
+\r
+ overallHorizon = anOverallHorizon;\r
+ throttleManager = new ThrottleManager(overallHorizon);\r
+ }\r
+\r
+ /** * {@inheritDoc} */\r
+ public boolean validate(String anExpression) {\r
+ List parts = StringRoutines.splitString(anExpression.trim(), ":");\r
+\r
+ try {\r
+ if (parts.size()==2) {\r
+ Integer.parseInt((String) parts.get(0));\r
+ Integer.parseInt((String) parts.get(1));\r
+\r
+ return true;\r
+ }\r
+ }\r
+ catch (Throwable t) {\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ *\r
+ */\r
+ public boolean test(String anExpression, Entity anEntity, Request aRequest) {\r
+ String ip = aRequest.getHeader("ip");\r
+ int limit;\r
+ long period;\r
+\r
+ List parts = StringRoutines.splitString(anExpression, ":");\r
+\r
+ try {\r
+ period = Integer.parseInt((String) parts.get(0))*1000*60;\r
+ limit = Integer.parseInt((String) parts.get(1));\r
+ }\r
+ catch (Throwable t) {\r
+ return false;\r
+ }\r
+\r
+ return throttleManager.addMessage(ip, anEntity, limit, period);\r
+ };\r
+\r
+ private class ThrottleManager {\r
+ private Map throttles;\r
+ private long overallHorizon;\r
+ private Thread cleanUpThread;\r
+\r
+ public ThrottleManager(long anOverallHorizon) {\r
+ throttles = new HashMap();\r
+ overallHorizon = anOverallHorizon;\r
+\r
+ cleanUpThread = new Thread() {\r
+ public void run() {\r
+ while (true) {\r
+ synchronized(throttles) {\r
+ List toDelete = new ArrayList();\r
+ Iterator i = throttles.entrySet().iterator();\r
+\r
+ while (i.hasNext()) {\r
+ Map.Entry entry = (Map.Entry) i.next();\r
+ try {\r
+ if (((Throttle) entry.getValue()).flush()) {\r
+ toDelete.add(entry.getKey());\r
+ }\r
+ }\r
+ catch (Throwable t) {\r
+ toDelete.add(entry.getKey());\r
+ }\r
+ }\r
+\r
+ i = toDelete.iterator();\r
+ while (i.hasNext()) {\r
+ throttles.remove(i.next());\r
+ }\r
+ }\r
+ try {\r
+ Thread.sleep(60*10*1000);\r
+ }\r
+ catch (InterruptedException e) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ };\r
+\r
+ cleanUpThread.setDaemon(true);\r
+ cleanUpThread.start();\r
+ }\r
+\r
+ public boolean addMessage(String anIP, Entity anEntity, int aLimit, long aPeriod) {\r
+ synchronized (throttles) {\r
+ Throttle throttle = (Throttle) throttles.get(anIP);\r
+\r
+ if (throttle==null) {\r
+ throttle = new Throttle(overallHorizon);\r
+ throttles.put(anIP, throttle);\r
+ }\r
+ return throttle.addMessage(anEntity, aLimit, aPeriod);\r
+ }\r
+ }\r
+\r
+ private class Throttle {\r
+ private List messages;\r
+ private long horizon;\r
+\r
+ public Throttle(long aHorizon) {\r
+ messages = new ArrayList();\r
+ horizon = aHorizon;\r
+ }\r
+\r
+ public boolean flush() {\r
+ long limit = System.currentTimeMillis() - horizon;\r
+\r
+ while (messages.size()>0 && ((Message) messages.get(0)).getTime()<=limit) {\r
+ messages.remove(0);\r
+ }\r
+\r
+ return messages.size()==0;\r
+ }\r
+\r
+ public boolean addMessage(Entity anEntity, int aLimit, long aPeriod) {\r
+ Message lastMessage=null;\r
+ if (messages.size()>0) {\r
+ lastMessage = (Message) messages.get(messages.size()-1);\r
+ }\r
+\r
+ Message newMessage = new Message(anEntity.getId(),\r
+ anEntity instanceof EntityComment, System.currentTimeMillis());\r
+\r
+ if (!newMessage.equals(lastMessage))\r
+ messages.add(newMessage);\r
+\r
+ if (messages.size()>=aLimit) {\r
+ Message message = (Message) messages.get(messages.size()-aLimit);\r
+ return (System.currentTimeMillis()-message.getTime())<aPeriod;\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ private class Message {\r
+ private String id;\r
+ private boolean isComment;\r
+ private long time;\r
+\r
+ public Message(String anId, boolean anIsComment, long aTime) {\r
+ id = anId;\r
+ isComment = anIsComment;\r
+ time = aTime;\r
+ }\r
+\r
+ public String getId() {\r
+ return id;\r
+ }\r
+\r
+ public boolean getIsComment() {\r
+ return isComment;\r
+ }\r
+\r
+ public long getTime() {\r
+ return time;\r
+ }\r
+\r
+ public int hashCode() {\r
+ return getId().hashCode();\r
+ }\r
+\r
+ public boolean equals(Object anObject) {\r
+ if (anObject instanceof Message) {\r
+ Message that = (Message) anObject;\r
+\r
+ if (that.getId().equals(getId()) && that.getIsComment() == getIsComment()) {\r
+ return true;\r
+ }\r
+ }\r
+\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ }\r
+}\r