aed4347751d40f10bd3106a44f6d6f1f3036327d
[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.GeneratorFormatAdapters;\r
54 import mir.util.StringRoutines;\r
55 import mircoders.entity.EntityComment;\r
56 import mircoders.entity.EntityContent;\r
57 import mircoders.entity.EntityUsers;\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     logComment(aComment, aRequest);\r
166 \r
167     try {\r
168       long time = System.currentTimeMillis();\r
169 \r
170       FilterRule filterRule = findMatchingFilter(aComment, aRequest);\r
171 \r
172       if (filterRule != null) {\r
173         logger.debug("Match for " + filterRule.getType() + " rule '" + filterRule.getExpression() + "'");\r
174         filterRule.setLastHit(new GregorianCalendar().getTime());\r
175         MirGlobal.performCommentOperation(null, aComment, filterRule.getCommentAction());\r
176         setCookie(aResponse);\r
177         save();\r
178       }\r
179 \r
180       logger.info("checkComment: " + (System.currentTimeMillis() - time) + "ms");\r
181     }\r
182     catch (Throwable t) {\r
183       t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));\r
184       logger.error("Abuse.checkComment: " + t.toString());\r
185     }\r
186   }\r
187 \r
188   public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {\r
189     logArticle(anArticle, aRequest);\r
190 \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         MirGlobal.performArticleOperation(null, anArticle, filterRule.getArticleAction());\r
200         setCookie(aResponse);\r
201         save();\r
202       }\r
203 \r
204       logger.info("checkArticle: " + (System.currentTimeMillis() - time) + "ms");\r
205     }\r
206     catch (Throwable t) {\r
207       t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));\r
208       logger.error("Abuse.checkArticle: " + t.toString());\r
209     }\r
210   }\r
211 \r
212   public boolean getLogEnabled() {\r
213     return logEnabled;\r
214   }\r
215 \r
216   public void setLogEnabled(boolean anEnabled) {\r
217     if (!configuration.getString("Abuse.DisallowIPLogging", "0").equals("1"))\r
218       logEnabled = anEnabled;\r
219     truncateLog();\r
220   }\r
221 \r
222   public int getLogSize() {\r
223     return logSize;\r
224   }\r
225 \r
226   public void setLogSize(int aSize) {\r
227     logSize = aSize;\r
228     truncateLog();\r
229   }\r
230 \r
231   public boolean getOpenPostingDisabled() {\r
232     return openPostingDisabled;\r
233   }\r
234 \r
235   public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {\r
236     openPostingDisabled = anOpenPostingDisabled;\r
237   }\r
238 \r
239   public boolean getOpenPostingPassword() {\r
240     return openPostingPassword;\r
241   }\r
242 \r
243   public void setOpenPostingPassword(boolean anOpenPostingPassword) {\r
244     openPostingPassword = anOpenPostingPassword;\r
245   }\r
246 \r
247   public boolean getCookieOnBlock() {\r
248     return cookieOnBlock;\r
249   }\r
250 \r
251   public void setCookieOnBlock(boolean aCookieOnBlock) {\r
252     cookieOnBlock = aCookieOnBlock;\r
253   }\r
254 \r
255   public String getArticleBlockAction() {\r
256     return articleBlockAction;\r
257   }\r
258 \r
259   public void setArticleBlockAction(String anAction) {\r
260     articleBlockAction = anAction;\r
261   }\r
262 \r
263   public String getCommentBlockAction() {\r
264     return commentBlockAction;\r
265   }\r
266 \r
267   public void setCommentBlockAction(String anAction) {\r
268     commentBlockAction = anAction;\r
269   }\r
270 \r
271   public List getLog() {\r
272     synchronized (log) {\r
273       try {\r
274         List result = new Vector();\r
275 \r
276         Iterator i = log.iterator();\r
277         while (i.hasNext()) {\r
278           LogEntry logEntry = (LogEntry) i.next();\r
279           Map entry = new HashMap();\r
280 \r
281           entry.put("ip", logEntry.getIpNumber());\r
282           entry.put("id", logEntry.getId());\r
283           entry.put("timestamp", new GeneratorFormatAdapters.DateFormatAdapter(logEntry.getTimeStamp(), MirPropertiesConfiguration.instance().getString("Mir.DefaultTimezone")));\r
284           if (logEntry.getIsArticle())\r
285             entry.put("type", "content");\r
286           else\r
287             entry.put("type", "comment");\r
288           entry.put("browser", logEntry.getBrowserString());\r
289 \r
290           result.add(entry);\r
291         }\r
292 \r
293         return result;\r
294       }\r
295       catch (Throwable t) {\r
296         throw new RuntimeException(t.toString());\r
297       }\r
298     }\r
299   }\r
300 \r
301   public void logComment(Entity aComment, Request aRequest) {\r
302     String ipAddress = aRequest.getHeader("ip");\r
303     String id = aComment.getId();\r
304     String browser = aRequest.getHeader("User-Agent");\r
305 \r
306     logComment(ipAddress, id, new Date(), browser);\r
307   }\r
308 \r
309   public void logArticle(Entity anArticle, Request aRequest) {\r
310     String ipAddress = aRequest.getHeader("ip");\r
311     String id = anArticle.getId();\r
312     String browser = aRequest.getHeader("User-Agent");\r
313 \r
314     logArticle(ipAddress, id, new Date(), browser);\r
315   }\r
316 \r
317   public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser) {\r
318     appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false));\r
319   }\r
320 \r
321   public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser) {\r
322     appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true));\r
323   }\r
324 \r
325   public void load() {\r
326     synchronized (filterRules) {\r
327       try {\r
328         ExtendedProperties configuration = new ExtendedProperties();\r
329 \r
330         try {\r
331           configuration = new ExtendedProperties(configFile);\r
332         }\r
333         catch (FileNotFoundException e) {\r
334         }\r
335 \r
336         getFilterConfig(filterRules, "abuse.filter", configuration);\r
337 \r
338         setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));\r
339         setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));\r
340         setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));\r
341         setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));\r
342         setLogSize(configuration.getInt("abuse.logSize", 10));\r
343         setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));\r
344         setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));\r
345       }\r
346       catch (Throwable t) {\r
347         throw new RuntimeException(t.toString());\r
348       }\r
349     }\r
350   }\r
351 \r
352   public void save() {\r
353     synchronized (filterRules) {\r
354       try {\r
355         ExtendedProperties configuration = new ExtendedProperties();\r
356 \r
357         setFilterConfig(filterRules, "abuse.filter", configuration);\r
358 \r
359         configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled() ? "1" : "0");\r
360         configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword() ? "1" : "0");\r
361         configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock() ? "1" : "0");\r
362         configuration.addProperty("abuse.logEnabled", getLogEnabled() ? "1" : "0");\r
363         configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));\r
364         configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());\r
365         configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());\r
366 \r
367         configuration.save(new FileOutputStream(new File(configFile)), "Anti abuse configuration");\r
368       }\r
369       catch (Throwable t) {\r
370         throw new RuntimeException(t.toString());\r
371       }\r
372     }\r
373   }\r
374 \r
375   public List getFilterTypes() {\r
376     try {\r
377       List result = new Vector();\r
378 \r
379       Iterator i = filterTypeIds.iterator();\r
380       while (i.hasNext()) {\r
381         String id = (String) i.next();\r
382 \r
383         Map action = new HashMap();\r
384         action.put("resource", id);\r
385         action.put("identifier", id);\r
386 \r
387         result.add(action);\r
388       }\r
389 \r
390       return result;\r
391     }\r
392     catch (Throwable t) {\r
393       throw new RuntimeException("can't get article actions");\r
394     }\r
395   }\r
396 \r
397   public List getArticleActions() {\r
398     try {\r
399       List result = new Vector();\r
400 \r
401       Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();\r
402       while (i.hasNext()) {\r
403         MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =\r
404             (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();\r
405 \r
406         Map action = new HashMap();\r
407         action.put("resource", operation.getName());\r
408         action.put("identifier", operation.getName());\r
409 \r
410         result.add(action);\r
411       }\r
412 \r
413       return result;\r
414     }\r
415     catch (Throwable t) {\r
416       throw new RuntimeException("can't get article actions");\r
417     }\r
418   }\r
419 \r
420   public List getCommentActions() {\r
421     try {\r
422       List result = new Vector();\r
423 \r
424       Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();\r
425       while (i.hasNext()) {\r
426         MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =\r
427             (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();\r
428 \r
429         Map action = new HashMap();\r
430         action.put("resource", operation.getName());\r
431         action.put("identifier", operation.getName());\r
432 \r
433         result.add(action);\r
434       }\r
435 \r
436       return result;\r
437     }\r
438     catch (Throwable t) {\r
439       throw new RuntimeException("can't get comment actions");\r
440     }\r
441   }\r
442 \r
443   public List getFilters() {\r
444     List result = new Vector();\r
445 \r
446     synchronized (filterRules) {\r
447       Iterator i = filterRules.iterator();\r
448       while (i.hasNext()) {\r
449         FilterRule filter = (FilterRule) i.next();\r
450         result.add(filter.clone());\r
451       }\r
452       return result;\r
453     }\r
454   }\r
455 \r
456   public String addFilter(String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {\r
457     return addFilter(aType, anExpression, aComments, aCommentAction, anArticleAction, null);\r
458   }\r
459 \r
460   public String addFilter(String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction, Date aListHit) {\r
461     return addFilter(filterRules, aType, anExpression, aComments, aCommentAction, anArticleAction, aListHit);\r
462   }\r
463 \r
464   public FilterRule getFilter(String anId) {\r
465     synchronized (filterRules) {\r
466       FilterRule result = (FilterRule) findFilter(filterRules, anId);\r
467       if (result == null)\r
468         return result;\r
469       else\r
470         return (FilterRule) result.clone();\r
471     }\r
472   }\r
473 \r
474   public String setFilter(String anIdentifier, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {\r
475     return setFilter(filterRules, anIdentifier, aType, anExpression, aComments, aCommentAction, anArticleAction);\r
476   }\r
477 \r
478   public void deleteFilter(String anIdentifier) {\r
479     deleteFilter(filterRules, anIdentifier);\r
480   }\r
481 \r
482   public void moveFilterUp(String anIdentifier) {\r
483     moveFilter(filterRules, anIdentifier, -1);\r
484   }\r
485 \r
486   public void moveFilterDown(String anIdentifier) {\r
487     moveFilter(filterRules, anIdentifier, 1);\r
488   }\r
489 \r
490   private String addFilter(List aFilters, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction, Date aLastHit) {\r
491     MirAntiAbuseFilterType type = (MirAntiAbuseFilterType) filterTypes.get(aType);\r
492 \r
493     if (type == null)\r
494       return "invalidtype";\r
495 \r
496     if (!type.validate(anExpression)) {\r
497       return "invalidexpression";\r
498     }\r
499 \r
500     FilterRule filter = new FilterRule();\r
501 \r
502     filter.setId(generateId());\r
503     filter.setExpression(anExpression);\r
504     filter.setType(aType);\r
505     filter.setComments(aComments);\r
506     filter.setArticleAction(anArticleAction);\r
507     filter.setCommentAction(aCommentAction);\r
508     filter.setLastHit(aLastHit);\r
509 \r
510     synchronized (aFilters) {\r
511       aFilters.add(filter);\r
512     }\r
513 \r
514     return null;\r
515   }\r
516 \r
517   private String setFilter(List aFilters, String anIdentifier, String aType, String anExpression, String aComments, String aCommentAction, String anArticleAction) {\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     synchronized (aFilters) {\r
528       FilterRule filter = findFilter(aFilters, anIdentifier);\r
529 \r
530       if (filter != null) {\r
531         filter.setExpression(anExpression);\r
532         filter.setType(aType);\r
533         filter.setCommentAction(aCommentAction);\r
534         filter.setArticleAction(anArticleAction);\r
535         filter.setComments(aComments);\r
536       }\r
537 \r
538       return null;\r
539     }\r
540   }\r
541 \r
542   private FilterRule findFilter(List aFilters, String anIdentifier) {\r
543     synchronized (aFilters) {\r
544       Iterator i = aFilters.iterator();\r
545       while (i.hasNext()) {\r
546         FilterRule filter = (FilterRule) i.next();\r
547 \r
548         if (filter.getId().equals(anIdentifier)) {\r
549           return filter;\r
550         }\r
551       }\r
552     }\r
553 \r
554     return null;\r
555   }\r
556 \r
557   private void moveFilter(List aFilters, String anIdentifier, int aDirection) {\r
558     synchronized (aFilters) {\r
559       for (int i = 0; i < aFilters.size(); i++) {\r
560         FilterRule rule = (FilterRule) aFilters.get(i);\r
561 \r
562         if (rule.getId().equals(anIdentifier) && (i + aDirection >= 0) && (i + aDirection < aFilters.size())) {\r
563           aFilters.remove(rule);\r
564           aFilters.add(i + aDirection, rule);\r
565           break;\r
566         }\r
567       }\r
568     }\r
569   }\r
570 \r
571   private void deleteFilter(List aFilters, String anIdentifier) {\r
572     synchronized (aFilters) {\r
573       FilterRule filter = findFilter(aFilters, anIdentifier);\r
574 \r
575       if (filter != null) {\r
576         aFilters.remove(filter);\r
577       }\r
578     }\r
579   }\r
580 \r
581   private String generateId() {\r
582     synchronized (this) {\r
583       maxIdentifier = maxIdentifier + 1;\r
584 \r
585       return Integer.toString(maxIdentifier);\r
586     }\r
587   }\r
588 \r
589   public class FilterRule {\r
590     private String identifier;\r
591     private String expression;\r
592     private String type;\r
593     private String comments;\r
594     private String articleAction;\r
595     private String commentAction;\r
596     private Date lastHit;\r
597 \r
598     public FilterRule() {\r
599       expression = "";\r
600       type = "";\r
601       identifier = "";\r
602       comments = "";\r
603       articleAction = articleBlockAction;\r
604       commentAction = commentBlockAction;\r
605       lastHit = null;\r
606     }\r
607 \r
608     public Date getLastHit() {\r
609       return lastHit;\r
610     }\r
611 \r
612     public void setLastHit(Date aDate) {\r
613       lastHit = aDate;\r
614     }\r
615 \r
616     public String getId() {\r
617       return identifier;\r
618     }\r
619 \r
620     public void setId(String anId) {\r
621       identifier = anId;\r
622     }\r
623 \r
624     public String getExpression() {\r
625       return expression;\r
626     }\r
627 \r
628     public void setExpression(String anExpression) {\r
629       expression = anExpression;\r
630     }\r
631 \r
632     public String getType() {\r
633       return type;\r
634     }\r
635 \r
636     public void setType(String aType) {\r
637       type = aType;\r
638     }\r
639 \r
640     public void setComments(String aComments) {\r
641       comments = aComments;\r
642     }\r
643 \r
644     public String getComments() {\r
645       return comments;\r
646     }\r
647 \r
648     public String getArticleAction() {\r
649       return articleAction;\r
650     }\r
651 \r
652     public void setArticleAction(String anArticleAction) {\r
653       articleAction = anArticleAction;\r
654     }\r
655 \r
656     public String getCommentAction() {\r
657       return commentAction;\r
658     }\r
659 \r
660     public void setCommentAction(String aCommentAction) {\r
661       commentAction = aCommentAction;\r
662     }\r
663 \r
664     public boolean test(Entity anEntity, Request aRequest) {\r
665       MirAntiAbuseFilterType filterType = (MirAntiAbuseFilterType) filterTypes.get(type);\r
666       try {\r
667         if (filterType != null)\r
668           return filterType.test(expression, anEntity, aRequest);\r
669       }\r
670       catch (Throwable t) {\r
671         logger.error("error while testing " + type + "-filter '" + expression + "'");\r
672       }\r
673 \r
674       return false;\r
675     };\r
676 \r
677     public Object clone() {\r
678       FilterRule result = new FilterRule();\r
679       result.setComments(getComments());\r
680       result.setExpression(getExpression());\r
681       result.setId(getId());\r
682       result.setType(getType());\r
683       result.setArticleAction(getArticleAction());\r
684       result.setCommentAction(getCommentAction());\r
685       result.setLastHit(getLastHit());\r
686 \r
687       return result;\r
688     }\r
689   }\r
690 \r
691   private String escapeFilterPart(String aFilterPart) {\r
692     return StringRoutines.replaceStringCharacters(aFilterPart,\r
693                                                   new char[] {'\\', ':', '\n', '\r', '\t', ' '}\r
694                                                   ,\r
695                                                   new String[] {"\\\\", "\\:", "\\n", "\\r", "\\t", "\\ "});\r
696   }\r
697 \r
698   private String deescapeFilterPart(String aFilterPart) {\r
699     return StringRoutines.replaceEscapedStringCharacters(aFilterPart,\r
700         '\\',\r
701         new char[] {'\\', ':', 'n', 'r', 't', ' '}\r
702         ,\r
703         new String[] {"\\", ":", "\n", "\r", "\t", " "});\r
704   }\r
705 \r
706   private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {\r
707     synchronized (aFilters) {\r
708       Iterator i = aFilters.iterator();\r
709 \r
710       while (i.hasNext()) {\r
711         FilterRule filter = (FilterRule) i.next();\r
712 \r
713         String filterconfig =\r
714             escapeFilterPart(filter.getType()) + ":" +\r
715             escapeFilterPart(filter.getExpression()) + ":" +\r
716             escapeFilterPart(filter.getArticleAction()) + ":" +\r
717             escapeFilterPart(filter.getCommentAction()) + ":" +\r
718             escapeFilterPart(filter.getComments()) + ":";\r
719 \r
720         if (filter.getLastHit() != null)\r
721           filterconfig = filterconfig + filter.getLastHit().getTime();\r
722 \r
723         aConfiguration.addProperty(aConfigKey, filterconfig);\r
724       }\r
725     }\r
726   }\r
727 \r
728   private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {\r
729     synchronized (aFilters) {\r
730       aFilters.clear();\r
731 \r
732       if (aConfiguration.getStringArray(aConfigKey) != null) {\r
733 \r
734         Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).\r
735             iterator();\r
736 \r
737         while (i.hasNext()) {\r
738           String filter = (String) i.next();\r
739           List parts = StringRoutines.splitStringWithEscape(filter, ':', '\\');\r
740           if (parts.size() == 2) {\r
741             parts.add(articleBlockAction);\r
742             parts.add(commentBlockAction);\r
743             parts.add("");\r
744             parts.add("");\r
745           }\r
746 \r
747           if (parts.size() >= 5) {\r
748             Date lastHit = null;\r
749 \r
750             if (parts.size() >= 6) {\r
751               String lastHitString = (String) parts.get(5);\r
752 \r
753               try {\r
754                 lastHit = new Date(Long.parseLong(lastHitString));\r
755               }\r
756               catch (Throwable t) {\r
757               }\r
758             }\r
759 \r
760             addFilter(deescapeFilterPart( (String) parts.get(0)),\r
761                       deescapeFilterPart( (String) parts.get(1)),\r
762                       deescapeFilterPart( (String) parts.get(4)),\r
763                       deescapeFilterPart( (String) parts.get(3)),\r
764                       deescapeFilterPart( (String) parts.get(2)), lastHit);\r
765           }\r
766         }\r
767       }\r
768     }\r
769   }\r
770 \r
771   private static class LogEntry {\r
772     private String ipNumber;\r
773     private String browserString;\r
774     private String id;\r
775     private Date timeStamp;\r
776     private boolean isArticle;\r
777 \r
778     public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {\r
779       ipNumber = anIpNumber;\r
780       browserString = aBrowserString;\r
781       id = anId;\r
782       isArticle = anIsArticle;\r
783       timeStamp = aTimeStamp;\r
784     }\r
785 \r
786     public String getIpNumber() {\r
787       return ipNumber;\r
788     }\r
789 \r
790     public String getBrowserString() {\r
791       return browserString;\r
792     }\r
793 \r
794     public String getId() {\r
795       return id;\r
796     }\r
797 \r
798     public Date getTimeStamp() {\r
799       return timeStamp;\r
800     }\r
801 \r
802     public boolean getIsArticle() {\r
803       return isArticle;\r
804     }\r
805   }\r
806 \r
807   private void truncateLog() {\r
808     synchronized (log) {\r
809       if (!logEnabled)\r
810         log.clear();\r
811       else {\r
812         while (log.size() > 0 && log.size() > logSize) {\r
813           log.remove(0);\r
814         }\r
815       }\r
816     }\r
817   };\r
818 \r
819   private void appendLog(LogEntry anEntry) {\r
820     synchronized (log) {\r
821       if (logEnabled) {\r
822         log.add(anEntry);\r
823         truncateLog();\r
824       }\r
825     }\r
826   }\r
827 }\r