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