c5cee479908f2fb32d09aeda0bd1c4b5b0774075
[mir.git] / source / mircoders / global / Abuse.java
1 /*\r
2  * Copyright (C) 2001, 2002 The Mir-coders group\r
3  *\r
4  * This file is part of Mir.\r
5  *\r
6  * Mir is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * Mir is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with Mir; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  *\r
20  * In addition, as a special exception, The Mir-coders gives permission to link\r
21  * the code of this program with  any library licensed under the Apache Software License,\r
22  * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library\r
23  * (or with modified versions of the above that use the same license as the above),\r
24  * and distribute linked combinations including the two.  You must obey the\r
25  * GNU General Public License in all respects for all of the code used other than\r
26  * the above mentioned libraries.  If you modify this file, you may extend this\r
27  * exception to your version of the file, but you are not obligated to do so.\r
28  * If you do not wish to do so, delete this exception statement from your version.\r
29  */\r
30 \r
31 package mircoders.global;\r
32 \r
33 import java.io.File;\r
34 import java.io.FileNotFoundException;\r
35 import java.io.FileOutputStream;\r
36 import java.util.Arrays;\r
37 import java.util.Date;\r
38 import java.util.GregorianCalendar;\r
39 import java.util.HashMap;\r
40 import java.util.Iterator;\r
41 import java.util.List;\r
42 import java.util.Map;\r
43 import java.util.Random;\r
44 import java.util.Vector;\r
45 import javax.servlet.http.Cookie;\r
46 import javax.servlet.http.HttpServletResponse;\r
47 \r
48 import org.apache.commons.collections.ExtendedProperties;\r
49 import mir.config.MirPropertiesConfiguration;\r
50 import mir.entity.Entity;\r
51 import mir.log.LoggerWrapper;\r
52 import mir.session.Request;\r
53 import mir.util.DateTimeFunctions;\r
54 import mir.util.GeneratorFormatAdapters;\r
55 import mir.util.StringRoutines;\r
56 import mircoders.entity.EntityComment;\r
57 import mircoders.entity.EntityContent;\r
58 import mircoders.localizer.MirAdminInterfaceLocalizer;\r
59 import mircoders.localizer.MirAntiAbuseFilterType;\r
60 \r
61 \r
62 public class Abuse {\r
63   private List filterRules;\r
64   private Map filterTypes;\r
65   private List filterTypeIds;\r
66   private int maxIdentifier;\r
67   private LoggerWrapper logger;\r
68   private int logSize;\r
69   private boolean logEnabled;\r
70   private boolean openPostingDisabled;\r
71   private boolean openPostingPassword;\r
72   private boolean cookieOnBlock;\r
73   private String articleBlockAction;\r
74   private String commentBlockAction;\r
75   private List log;\r
76   private String configFile = MirGlobal.config().getStringWithHome("Abuse.Config");\r
77 \r
78   private MirPropertiesConfiguration configuration;\r
79 \r
80   private static String cookieName = MirGlobal.config().getString("Abuse.CookieName");\r
81   private static int cookieMaxAge = 60 * 60 * MirGlobal.config().getInt("Abuse.CookieMaxAge");\r
82 \r
83   public Abuse() {\r
84     logger = new LoggerWrapper("Global.Abuse");\r
85     filterRules = new Vector();\r
86     maxIdentifier = 0;\r
87     log = new Vector();\r
88 \r
89     try {\r
90       configuration = MirPropertiesConfiguration.instance();\r
91     }\r
92     catch (Throwable e) {\r
93       throw new RuntimeException("Can't get configuration: " + e.getMessage());\r
94     }\r
95 \r
96     logSize = 100;\r
97     logEnabled = false;\r
98     articleBlockAction = "";\r
99     commentBlockAction = "";\r
100     openPostingPassword = false;\r
101     openPostingDisabled = false;\r
102     cookieOnBlock = false;\r
103 \r
104     try {\r
105       filterTypes = new HashMap();\r
106       filterTypeIds = new Vector();\r
107 \r
108       Iterator i = MirGlobal.localizer().openPostings().getAntiAbuseFilterTypes().iterator();\r
109 \r
110       while (i.hasNext()) {\r
111         MirAntiAbuseFilterType filterType = (MirAntiAbuseFilterType) i.next();\r
112         filterTypes.put(filterType.getName(), filterType);\r
113         filterTypeIds.add(filterType.getName());\r
114       }\r
115     }\r
116     catch (Throwable t) {\r
117       throw new RuntimeException("Can't get filter types: " + t.getMessage());\r
118     }\r
119 \r
120     load();\r
121   }\r
122 \r
123   private void setCookie(HttpServletResponse aResponse) {\r
124     Random random = new Random();\r
125 \r
126     Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000)));\r
127     cookie.setMaxAge(cookieMaxAge);\r
128     cookie.setPath("/");\r
129 \r
130     if (aResponse != null)\r
131       aResponse.addCookie(cookie);\r
132   }\r
133 \r
134   private boolean checkCookie(List aCookies) {\r
135     if (getCookieOnBlock()) {\r
136       Iterator i = aCookies.iterator();\r
137 \r
138       while (i.hasNext()) {\r
139         Cookie cookie = (Cookie) i.next();\r
140 \r
141         if (cookie.getName().equals(cookieName)) {\r
142           logger.debug("cookie match");\r
143           return true;\r
144         }\r
145       }\r
146     }\r
147 \r
148     return false;\r
149   }\r
150 \r
151   FilterRule findMatchingFilter(Entity anEntity, Request aRequest) {\r
152     Iterator iterator = filterRules.iterator();\r
153 \r
154     while (iterator.hasNext()) {\r
155       FilterRule rule = (FilterRule) iterator.next();\r
156 \r
157       if (rule.test(anEntity, aRequest))\r
158         return rule;\r
159     }\r
160 \r
161     return null;\r
162   }\r
163 \r
164   public void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) {\r
165     try {\r
166       long time = System.currentTimeMillis();\r
167 \r
168       FilterRule filterRule = findMatchingFilter(aComment, aRequest);\r
169 \r
170       if (filterRule != null) {\r
171         logger.debug("Match for " + filterRule.getType() + " rule '" + filterRule.getExpression() + "'");\r
172         filterRule.setLastHit(new GregorianCalendar().getTime());\r
173         MirGlobal.performCommentOperation(null, aComment, filterRule.getCommentAction());\r
174         setCookie(aResponse);\r
175         save();\r
176         logComment(aComment, aRequest, filterRule.getType(), filterRule.getExpression());\r
177       }\r
178       else\r
179         logComment(aComment, aRequest);\r
180 \r
181 \r
182       logger.info("checkComment: " + (System.currentTimeMillis() - time) + "ms");\r
183     }\r
184     catch (Throwable t) {\r
185       t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));\r
186       logger.error("Abuse.checkComment: " + t.toString());\r
187     }\r
188   }\r
189 \r
190   public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {\r
191     try {\r
192       long time = System.currentTimeMillis();\r
193 \r
194       FilterRule filterRule = findMatchingFilter(anArticle, aRequest);\r
195 \r
196       if (filterRule != null) {\r
197         logger.debug("Match for " + filterRule.getType() + " rule '" + filterRule.getExpression() + "'");\r
198         filterRule.setLastHit(new GregorianCalendar().getTime());\r
199 \r
200         StringBuffer line = new StringBuffer();\r
201 \r
202         line.append(DateTimeFunctions.advancedDateFormat(\r
203             configuration.getString("Mir.DefaultDateTimeFormat"),\r
204             (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));\r
205 \r
206         line.append(" ");\r
207         line.append("filter");\r
208 \r
209         line.append(" ");\r
210         line.append(filterRule.getType() +" ("+ filterRule.getExpression()+")");\r
211         anArticle.appendToComments(line.toString());\r
212 \r
213         MirGlobal.performArticleOperation(null, anArticle, filterRule.getArticleAction());\r
214         setCookie(aResponse);\r
215         save();\r
216         logArticle(anArticle, aRequest, filterRule.getType(), filterRule.getExpression());\r
217       }\r
218       else\r
219         logArticle(anArticle, aRequest);\r
220 \r
221       logger.info("checkArticle: " + (System.currentTimeMillis() - time) + "ms");\r
222     }\r
223     catch (Throwable t) {\r
224       t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));\r
225       logger.error("Abuse.checkArticle: " + t.toString());\r
226     }\r
227   }\r
228 \r
229   public boolean getLogEnabled() {\r
230     return logEnabled;\r
231   }\r
232 \r
233   public void setLogEnabled(boolean anEnabled) {\r
234     if (!configuration.getString("Abuse.DisallowIPLogging", "0").equals("1"))\r
235       logEnabled = anEnabled;\r
236     truncateLog();\r
237   }\r
238 \r
239   public int getLogSize() {\r
240     return logSize;\r
241   }\r
242 \r
243   public void setLogSize(int aSize) {\r
244     logSize = aSize;\r
245     truncateLog();\r
246   }\r
247 \r
248   public boolean getOpenPostingDisabled() {\r
249     return openPostingDisabled;\r
250   }\r
251 \r
252   public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {\r
253     openPostingDisabled = anOpenPostingDisabled;\r
254   }\r
255 \r
256   public boolean getOpenPostingPassword() {\r
257     return openPostingPassword;\r
258   }\r
259 \r
260   public void setOpenPostingPassword(boolean anOpenPostingPassword) {\r
261     openPostingPassword = anOpenPostingPassword;\r
262   }\r
263 \r
264   public boolean getCookieOnBlock() {\r
265     return cookieOnBlock;\r
266   }\r
267 \r
268   public void setCookieOnBlock(boolean aCookieOnBlock) {\r
269     cookieOnBlock = aCookieOnBlock;\r
270   }\r
271 \r
272   public String getArticleBlockAction() {\r
273     return articleBlockAction;\r
274   }\r
275 \r
276   public void setArticleBlockAction(String anAction) {\r
277     articleBlockAction = anAction;\r
278   }\r
279 \r
280   public String getCommentBlockAction() {\r
281     return commentBlockAction;\r
282   }\r
283 \r
284   public void setCommentBlockAction(String anAction) {\r
285     commentBlockAction = anAction;\r
286   }\r
287 \r
288   public List getLog() {\r
289     synchronized (log) {\r
290       try {\r
291         List result = new Vector();\r
292 \r
293         Iterator i = log.iterator();\r
294         while (i.hasNext()) {\r
295           LogEntry logEntry = (LogEntry) i.next();\r
296           Map entry = new HashMap();\r
297 \r
298           entry.put("ip", logEntry.getIpNumber());\r
299           entry.put("id", logEntry.getId());\r
300           entry.put("timestamp", new GeneratorFormatAdapters.DateFormatAdapter(logEntry.getTimeStamp(), MirPropertiesConfiguration.instance().getString("Mir.DefaultTimezone")));\r
301           if (logEntry.getIsArticle())\r
302             entry.put("type", "content");\r
303           else\r
304             entry.put("type", "comment");\r
305           entry.put("browser", logEntry.getBrowserString());\r
306           entry.put("hitfiltertype", logEntry.getHitFilterType());\r
307           entry.put("hitfilterexpression", logEntry.getHitFilterExpression());\r
308 \r
309           result.add(entry);\r
310         }\r
311 \r
312         return result;\r
313       }\r
314       catch (Throwable t) {\r
315         throw new RuntimeException(t.toString());\r
316       }\r
317     }\r
318   }\r
319 \r
320   public void logComment(Entity aComment, Request aRequest) {\r
321     logComment(aComment, aRequest, null, null);\r
322   }\r
323 \r
324   public void logComment(Entity aComment, Request aRequest, String aHitFilterType, String aHitFilterExpression) {\r
325     String ipAddress = aRequest.getHeader("ip");\r
326     String id = aComment.getId();\r
327     String browser = aRequest.getHeader("User-Agent");\r
328 \r
329     logComment(ipAddress, id, new Date(), browser, aHitFilterType, aHitFilterExpression);\r
330   }\r
331 \r
332   public void logArticle(Entity anArticle, Request aRequest) {\r
333     logArticle(anArticle, aRequest, null, null);\r
334   }\r
335 \r
336   public void logArticle(Entity anArticle, Request aRequest, String aHitFilterType, String aHitFilterExpression) {\r
337     String ipAddress = aRequest.getHeader("ip");\r
338     String id = anArticle.getId();\r
339     String browser = aRequest.getHeader("User-Agent");\r
340 \r
341     logArticle(ipAddress, id, new Date(), browser, aHitFilterType, aHitFilterExpression);\r
342   }\r
343 \r
344   public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser, String aHitFilterType, String aHitFilterExpression) {\r
345     appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false, aHitFilterType, aHitFilterExpression));\r
346   }\r
347 \r
348   public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser, String aHitFilterType, String aHitFilterExpression) {\r
349     appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true, aHitFilterType, aHitFilterExpression));\r
350   }\r
351 \r
352   public void load() {\r
353     synchronized (filterRules) {\r
354       try {\r
355         ExtendedProperties configuration = new ExtendedProperties();\r
356 \r
357         try {\r
358           configuration = new ExtendedProperties(configFile);\r
359         }\r
360         catch (FileNotFoundException e) {\r
361         }\r
362 \r
363         getFilterConfig(filterRules, "abuse.filter", configuration);\r
364 \r
365         setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));\r
366         setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));\r
367         setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));\r
368         setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));\r
369         setLogSize(configuration.getInt("abuse.logSize", 10));\r
370         setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));\r
371         setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));\r
372       }\r
373       catch (Throwable t) {\r
374         throw new RuntimeException(t.toString());\r
375       }\r
376     }\r
377   }\r
378 \r
379   public void save() {\r
380     synchronized (filterRules) {\r
381       try {\r
382         ExtendedProperties configuration = new ExtendedProperties();\r
383 \r
384         setFilterConfig(filterRules, "abuse.filter", configuration);\r
385 \r
386         configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled() ? "1" : "0");\r
387         configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword() ? "1" : "0");\r
388         configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock() ? "1" : "0");\r
389         configuration.addProperty("abuse.logEnabled", getLogEnabled() ? "1" : "0");\r
390         configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));\r
391         configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());\r
392         configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());\r
393 \r
394         configuration.save(new FileOutputStream(new File(configFile)), "Anti abuse configuration");\r
395       }\r
396       catch (Throwable t) {\r
397         throw new RuntimeException(t.toString());\r
398       }\r
399     }\r
400   }\r
401 \r
402   public List getFilterTypes() {\r
403     try {\r
404       List result = new Vector();\r
405 \r
406       Iterator i = filterTypeIds.iterator();\r
407       while (i.hasNext()) {\r
408         String id = (String) i.next();\r
409 \r
410         Map action = new HashMap();\r
411         action.put("resource", id);\r
412         action.put("identifier", id);\r
413 \r
414         result.add(action);\r
415       }\r
416 \r
417       return result;\r
418     }\r
419     catch (Throwable t) {\r
420       throw new RuntimeException("can't get article actions");\r
421     }\r
422   }\r
423 \r
424   public List getArticleActions() {\r
425     try {\r
426       List result = new Vector();\r
427 \r
428       Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();\r
429       while (i.hasNext()) {\r
430         MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =\r
431             (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();\r
432 \r
433         Map action = new HashMap();\r
434         action.put("resource", operation.getName());\r
435         action.put("identifier", operation.getName());\r
436 \r
437         result.add(action);\r
438       }\r
439 \r
440       return result;\r
441     }\r
442     catch (Throwable t) {\r
443       throw new RuntimeException("can't get article actions");\r
444     }\r
445   }\r
446 \r
447   public List getCommentActions() {\r
448     try {\r
449       List result = new Vector();\r
450 \r
451       Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();\r
452       while (i.hasNext()) {\r
453         MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =\r
454             (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();\r
455 \r
456         Map action = new HashMap();\r
457         action.put("resource", operation.getName());\r
458         action.put("identifier", operation.getName());\r
459 \r
460         result.add(action);\r
461       }\r
462 \r
463       return result;\r
464     }\r
465     catch (Throwable t) {\r
466       throw new RuntimeException("can't get comment actions");\r
467     }\r
468   }\r
469 \r
470   public List getFilters() {\r
471     List result = new Vector();\r
472 \r
473     synchronized (filterRules) {\r
474       Iterator i = filterRules.iterator();\r
475       while (i.hasNext()) {\r
476         FilterRule filter = (FilterRule) i.next();\r
477         result.add(filter.clone());\r
478       }\r
479       return result;\r
480     }\r
481   }\r
482 \r
483   public String addFilter(String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {\r
484     return addFilter(aType, anExpression, aComments, aCommentAction, anArticleAction, null);\r
485   }\r
486 \r
487   public String addFilter(String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction, Date aListHit) {\r
488     return addFilter(filterRules, aType, anExpression, aComments, aCommentAction, anArticleAction, aListHit);\r
489   }\r
490 \r
491   public FilterRule getFilter(String anId) {\r
492     synchronized (filterRules) {\r
493       FilterRule result = (FilterRule) findFilter(filterRules, anId);\r
494       if (result == null)\r
495         return result;\r
496       else\r
497         return (FilterRule) result.clone();\r
498     }\r
499   }\r
500 \r
501   public String setFilter(String anIdentifier, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {\r
502     return setFilter(filterRules, anIdentifier, aType, anExpression, aComments, aCommentAction, anArticleAction);\r
503   }\r
504 \r
505   public void deleteFilter(String anIdentifier) {\r
506     deleteFilter(filterRules, anIdentifier);\r
507   }\r
508 \r
509   public void moveFilterUp(String anIdentifier) {\r
510     moveFilter(filterRules, anIdentifier, -1);\r
511   }\r
512 \r
513   public void moveFilterDown(String anIdentifier) {\r
514     moveFilter(filterRules, anIdentifier, 1);\r
515   }\r
516 \r
517   private String addFilter(List aFilters, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction, Date aLastHit) {\r
518     MirAntiAbuseFilterType type = (MirAntiAbuseFilterType) filterTypes.get(aType);\r
519 \r
520     if (type == null)\r
521       return "invalidtype";\r
522 \r
523     if (!type.validate(anExpression)) {\r
524       return "invalidexpression";\r
525     }\r
526 \r
527     FilterRule filter = new FilterRule();\r
528 \r
529     filter.setId(generateId());\r
530     filter.setExpression(anExpression);\r
531     filter.setType(aType);\r
532     filter.setComments(aComments);\r
533     filter.setArticleAction(anArticleAction);\r
534     filter.setCommentAction(aCommentAction);\r
535     filter.setLastHit(aLastHit);\r
536 \r
537     synchronized (aFilters) {\r
538       aFilters.add(filter);\r
539     }\r
540 \r
541     return null;\r
542   }\r
543 \r
544   private String setFilter(List aFilters, String anIdentifier, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {\r
545     MirAntiAbuseFilterType type = (MirAntiAbuseFilterType) filterTypes.get(aType);\r
546 \r
547     if (type == null)\r
548       return "invalidtype";\r
549 \r
550     if (!type.validate(anExpression)) {\r
551       return "invalidexpression";\r
552     }\r
553 \r
554     synchronized (aFilters) {\r
555       FilterRule filter = findFilter(aFilters, anIdentifier);\r
556 \r
557       if (filter != null) {\r
558         filter.setExpression(anExpression);\r
559         filter.setType(aType);\r
560         filter.setCommentAction(aCommentAction);\r
561         filter.setArticleAction(anArticleAction);\r
562         filter.setComments(aComments);\r
563       }\r
564 \r
565       return null;\r
566     }\r
567   }\r
568 \r
569   private FilterRule findFilter(List aFilters, String anIdentifier) {\r
570     synchronized (aFilters) {\r
571       Iterator i = aFilters.iterator();\r
572       while (i.hasNext()) {\r
573         FilterRule filter = (FilterRule) i.next();\r
574 \r
575         if (filter.getId().equals(anIdentifier)) {\r
576           return filter;\r
577         }\r
578       }\r
579     }\r
580 \r
581     return null;\r
582   }\r
583 \r
584   private void moveFilter(List aFilters, String anIdentifier, int aDirection) {\r
585     synchronized (aFilters) {\r
586       for (int i = 0; i < aFilters.size(); i++) {\r
587         FilterRule rule = (FilterRule) aFilters.get(i);\r
588 \r
589         if (rule.getId().equals(anIdentifier) && (i + aDirection >= 0) && (i + aDirection < aFilters.size())) {\r
590           aFilters.remove(rule);\r
591           aFilters.add(i + aDirection, rule);\r
592           break;\r
593         }\r
594       }\r
595     }\r
596   }\r
597 \r
598   private void deleteFilter(List aFilters, String anIdentifier) {\r
599     synchronized (aFilters) {\r
600       FilterRule filter = findFilter(aFilters, anIdentifier);\r
601 \r
602       if (filter != null) {\r
603         aFilters.remove(filter);\r
604       }\r
605     }\r
606   }\r
607 \r
608   private String generateId() {\r
609     synchronized (this) {\r
610       maxIdentifier = maxIdentifier + 1;\r
611 \r
612       return Integer.toString(maxIdentifier);\r
613     }\r
614   }\r
615 \r
616   public class FilterRule {\r
617     private String identifier;\r
618     private String expression;\r
619     private String type;\r
620     private String comments;\r
621     private String articleAction;\r
622     private String commentAction;\r
623     private Date lastHit;\r
624 \r
625     public FilterRule() {\r
626       expression = "";\r
627       type = "";\r
628       identifier = "";\r
629       comments = "";\r
630       articleAction = articleBlockAction;\r
631       commentAction = commentBlockAction;\r
632       lastHit = null;\r
633     }\r
634 \r
635     public Date getLastHit() {\r
636       return lastHit;\r
637     }\r
638 \r
639     public void setLastHit(Date aDate) {\r
640       lastHit = aDate;\r
641     }\r
642 \r
643     public String getId() {\r
644       return identifier;\r
645     }\r
646 \r
647     public void setId(String anId) {\r
648       identifier = anId;\r
649     }\r
650 \r
651     public String getExpression() {\r
652       return expression;\r
653     }\r
654 \r
655     public void setExpression(String anExpression) {\r
656       expression = anExpression;\r
657     }\r
658 \r
659     public String getType() {\r
660       return type;\r
661     }\r
662 \r
663     public void setType(String aType) {\r
664       type = aType;\r
665     }\r
666 \r
667     public void setComments(String aComments) {\r
668       comments = aComments;\r
669     }\r
670 \r
671     public String getComments() {\r
672       return comments;\r
673     }\r
674 \r
675     public String getArticleAction() {\r
676       return articleAction;\r
677     }\r
678 \r
679     public void setArticleAction(String anArticleAction) {\r
680       articleAction = anArticleAction;\r
681     }\r
682 \r
683     public String getCommentAction() {\r
684       return commentAction;\r
685     }\r
686 \r
687     public void setCommentAction(String aCommentAction) {\r
688       commentAction = aCommentAction;\r
689     }\r
690 \r
691     public boolean test(Entity anEntity, Request aRequest) {\r
692       MirAntiAbuseFilterType filterType = (MirAntiAbuseFilterType) filterTypes.get(type);\r
693       try {\r
694         if (filterType != null)\r
695           return filterType.test(expression, anEntity, aRequest);\r
696       }\r
697       catch (Throwable t) {\r
698         logger.error("error while testing " + type + "-filter '" + expression + "'");\r
699       }\r
700 \r
701       return false;\r
702     };\r
703 \r
704     public Object clone() {\r
705       FilterRule result = new FilterRule();\r
706       result.setComments(getComments());\r
707       result.setExpression(getExpression());\r
708       result.setId(getId());\r
709       result.setType(getType());\r
710       result.setArticleAction(getArticleAction());\r
711       result.setCommentAction(getCommentAction());\r
712       result.setLastHit(getLastHit());\r
713 \r
714       return result;\r
715     }\r
716   }\r
717 \r
718   private String escapeFilterPart(String aFilterPart) {\r
719     return StringRoutines.replaceStringCharacters(aFilterPart,\r
720         new char[] {'\\', ':', '\n', '\r', '\t', ' '},\r
721         new String[] {"\\\\", "\\:", "\\n", "\\r", "\\t", "\\ "});\r
722   }\r
723 \r
724   private String deescapeFilterPart(String aFilterPart) {\r
725     return StringRoutines.replaceEscapedStringCharacters(aFilterPart,\r
726         '\\',\r
727         new char[] {'\\', ':', 'n', 'r', 't', ' '},\r
728         new String[] {"\\", ":", "\n", "\r", "\t", " "});\r
729   }\r
730 \r
731   private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {\r
732     synchronized (aFilters) {\r
733       Iterator i = aFilters.iterator();\r
734 \r
735       while (i.hasNext()) {\r
736         FilterRule filter = (FilterRule) i.next();\r
737 \r
738         String filterconfig =\r
739             escapeFilterPart(filter.getType()) + ":" +\r
740             escapeFilterPart(filter.getExpression()) + ":" +\r
741             escapeFilterPart(filter.getArticleAction()) + ":" +\r
742             escapeFilterPart(filter.getCommentAction()) + ":" +\r
743             escapeFilterPart(filter.getComments()) + ":";\r
744 \r
745         if (filter.getLastHit() != null)\r
746           filterconfig = filterconfig + filter.getLastHit().getTime();\r
747 \r
748         aConfiguration.addProperty(aConfigKey, filterconfig);\r
749       }\r
750     }\r
751   }\r
752 \r
753   private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {\r
754     synchronized (aFilters) {\r
755       aFilters.clear();\r
756 \r
757       if (aConfiguration.getStringArray(aConfigKey) != null) {\r
758 \r
759         Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).\r
760             iterator();\r
761 \r
762         while (i.hasNext()) {\r
763           String filter = (String) i.next();\r
764           List parts = StringRoutines.splitStringWithEscape(filter, ':', '\\');\r
765           if (parts.size() == 2) {\r
766             parts.add(articleBlockAction);\r
767             parts.add(commentBlockAction);\r
768             parts.add("");\r
769             parts.add("");\r
770           }\r
771 \r
772           if (parts.size() >= 5) {\r
773             Date lastHit = null;\r
774 \r
775             if (parts.size() >= 6) {\r
776               String lastHitString = (String) parts.get(5);\r
777 \r
778               try {\r
779                 lastHit = new Date(Long.parseLong(lastHitString));\r
780               }\r
781               catch (Throwable t) {\r
782               }\r
783             }\r
784 \r
785             addFilter(deescapeFilterPart( (String) parts.get(0)),\r
786                       deescapeFilterPart( (String) parts.get(1)),\r
787                       deescapeFilterPart( (String) parts.get(4)),\r
788                       deescapeFilterPart( (String) parts.get(3)),\r
789                       deescapeFilterPart( (String) parts.get(2)), lastHit);\r
790           }\r
791         }\r
792       }\r
793     }\r
794   }\r
795 \r
796   private static class LogEntry {\r
797     private String ipNumber;\r
798     private String browserString;\r
799     private String id;\r
800     private Date timeStamp;\r
801     private boolean isArticle;\r
802     private String hitFilterType;\r
803     private String hitFilterExpression;\r
804 \r
805     public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle, String aHitFilterType, String aHitFilterExpression) {\r
806       ipNumber = anIpNumber;\r
807       browserString = aBrowserString;\r
808       id = anId;\r
809       isArticle = anIsArticle;\r
810       timeStamp = aTimeStamp;\r
811       hitFilterType = aHitFilterType;\r
812       hitFilterExpression = aHitFilterExpression;\r
813     }\r
814 \r
815     public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {\r
816       this(aTimeStamp, anIpNumber, aBrowserString, anId, anIsArticle, null, null);\r
817     }\r
818 \r
819     public String getIpNumber() {\r
820       return ipNumber;\r
821     }\r
822 \r
823     public String getBrowserString() {\r
824       return browserString;\r
825     }\r
826 \r
827     public String getId() {\r
828       return id;\r
829     }\r
830 \r
831     public String getHitFilterType() {\r
832       return hitFilterType;\r
833     }\r
834 \r
835     public String getHitFilterExpression() {\r
836       return hitFilterExpression;\r
837     }\r
838 \r
839     public Date getTimeStamp() {\r
840       return timeStamp;\r
841     }\r
842 \r
843     public boolean getIsArticle() {\r
844       return isArticle;\r
845     }\r
846   }\r
847 \r
848   private void truncateLog() {\r
849     synchronized (log) {\r
850       if (!logEnabled)\r
851         log.clear();\r
852       else {\r
853         while (log.size() > 0 && log.size() > logSize) {\r
854           log.remove(0);\r
855         }\r
856       }\r
857     }\r
858   };\r
859 \r
860   private void appendLog(LogEntry anEntry) {\r
861     synchronized (log) {\r
862       if (logEnabled) {\r
863         log.add(anEntry);\r
864         truncateLog();\r
865       }\r
866     }\r
867   }\r
868 }\r