small fixes here and there
[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.HashMap;\r
39 import java.util.Iterator;\r
40 import java.util.List;\r
41 import java.util.Map;\r
42 import java.util.Random;\r
43 import java.util.Vector;\r
44 import javax.servlet.http.Cookie;\r
45 import javax.servlet.http.HttpServletRequest;\r
46 import javax.servlet.http.HttpServletResponse;\r
47 \r
48 import org.apache.commons.collections.ExtendedProperties;\r
49 import gnu.regexp.RE;\r
50 \r
51 import mir.config.MirPropertiesConfiguration;\r
52 import mir.entity.Entity;\r
53 import mir.log.LoggerWrapper;\r
54 import mir.session.HTTPAdapters;\r
55 import mir.session.Request;\r
56 import mir.util.GeneratorFormatAdapters;\r
57 import mir.util.InternetFunctions;\r
58 import mir.util.StringRoutines;\r
59 import mir.config.*;\r
60 import mircoders.entity.EntityComment;\r
61 import mircoders.entity.EntityContent;\r
62 import mircoders.entity.EntityUsers;\r
63 import mircoders.localizer.MirAdminInterfaceLocalizer;\r
64 \r
65 \r
66 public class Abuse {\r
67   private List filters;\r
68   private int maxIdentifier;\r
69   private LoggerWrapper logger;\r
70   private LoggerWrapper adminUsageLogger;\r
71   private int logSize;\r
72   private boolean logEnabled;\r
73   private boolean openPostingDisabled;\r
74   private boolean openPostingPassword;\r
75   private boolean cookieOnBlock;\r
76   private String articleBlockAction;\r
77   private String commentBlockAction;\r
78   private List log;\r
79   private String configFile = MirGlobal.config().getStringWithHome("Abuse.Config");\r
80 \r
81   private MirPropertiesConfiguration configuration;\r
82 \r
83 \r
84   private static final String IP_FILTER_TYPE="ip";\r
85   private static final String REGEXP_FILTER_TYPE="regexp";\r
86   private static String cookieName=MirGlobal.config().getString("Abuse.CookieName");\r
87   private static int cookieMaxAge = 60*60*MirGlobal.config().getInt("Abuse.CookieMaxAge");\r
88 \r
89   public Abuse() {\r
90     logger = new LoggerWrapper("Global.Abuse");\r
91     adminUsageLogger = new LoggerWrapper("AdminUsage");\r
92     filters = new Vector();\r
93     maxIdentifier = 0;\r
94     log = new Vector();\r
95 \r
96     try {\r
97       configuration = MirPropertiesConfiguration.instance();\r
98     }\r
99     catch (Throwable e) {\r
100       throw new RuntimeException("Can't get configuration: " + e.getMessage());\r
101     }\r
102 \r
103     logSize = 100;\r
104     logEnabled = false;\r
105     articleBlockAction = "";\r
106     commentBlockAction = "";\r
107     openPostingPassword = false;\r
108     openPostingDisabled = false;\r
109     cookieOnBlock = false;\r
110 \r
111     load();\r
112   }\r
113 \r
114   public boolean checkIpFilter(String anIpAddress) {\r
115     synchronized (filters) {\r
116       Iterator i = filters.iterator();\r
117 \r
118       while (i.hasNext()) {\r
119         Filter filter = (Filter) i.next();\r
120 \r
121         try {\r
122           if ( (filter.getType().equals(IP_FILTER_TYPE)) &&\r
123               InternetFunctions.isIpAddressInNetwork(anIpAddress, filter.getExpression())) {\r
124             logger.debug("ip match on " + filter.getExpression());\r
125             return true;\r
126           }\r
127         }\r
128         catch (Throwable t) {\r
129           logger.warn("error while checking ip address " + anIpAddress + " over network " + filter.expression + ": " + t.getMessage());\r
130         }\r
131       }\r
132 \r
133       return false;\r
134     }\r
135   }\r
136 \r
137   private boolean checkRegExpFilter(Entity anEntity) {\r
138     synchronized (filters) {\r
139       Iterator i = filters.iterator();\r
140 \r
141       while (i.hasNext()) {\r
142         Filter filter = (Filter) i.next();\r
143 \r
144         if (filter.getType().equals(REGEXP_FILTER_TYPE)) {\r
145           try {\r
146             RE regularExpression = new RE(filter.getExpression(), RE.REG_ICASE);\r
147 \r
148             Iterator j = anEntity.getFields().iterator();\r
149             while (j.hasNext()) {\r
150               String field = anEntity.getValue( (String) j.next());\r
151 \r
152               if (field != null && regularExpression.isMatch(field.toLowerCase())) {\r
153                 logger.debug("regexp match on " + filter.getExpression());\r
154                 return true;\r
155               }\r
156             }\r
157           }\r
158           catch (Throwable t) {\r
159             logger.warn("error while checking entity with regexp " + filter.getExpression() + ": " + t.getMessage());\r
160           }\r
161         }\r
162       }\r
163 \r
164       return false;\r
165     }\r
166   }\r
167 \r
168   private void setCookie(HttpServletResponse aResponse) {\r
169     Random random = new Random();\r
170 \r
171     Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000)));\r
172     cookie.setMaxAge(cookieMaxAge);\r
173     cookie.setPath("/");\r
174 \r
175     if (aResponse!=null)\r
176       aResponse.addCookie(cookie);\r
177   }\r
178 \r
179   private boolean checkCookie(List aCookies) {\r
180     if (getCookieOnBlock()) {\r
181       Iterator i = aCookies.iterator();\r
182 \r
183       while (i.hasNext()) {\r
184         Cookie cookie = (Cookie) i.next();\r
185 \r
186         if (cookie.getName().equals(cookieName)) {\r
187           logger.debug("cookie match");\r
188           return true;\r
189         }\r
190       }\r
191     }\r
192 \r
193     return false;\r
194   }\r
195 \r
196   public boolean checkRequest(Request aRequest, HttpServletResponse aResponse, String anId, boolean anIsComment) {\r
197     String address = "0.0.0.0";\r
198     String browser = "unknown";\r
199     List cookies = null;\r
200 \r
201     HttpServletRequest request = null;\r
202 \r
203     if (aRequest instanceof HTTPAdapters.HTTPParsedRequestAdapter) {\r
204       request = ((HTTPAdapters.HTTPParsedRequestAdapter) aRequest).getRequest();\r
205     }\r
206     else if (aRequest instanceof HTTPAdapters.HTTPRequestAdapter) {\r
207       request = ((HTTPAdapters.HTTPRequestAdapter) aRequest).getRequest();\r
208     }\r
209     if (request!=null) {\r
210       browser = (String) request.getHeader("User-Agent");\r
211       address = request.getRemoteAddr();\r
212       if (request.getCookies()!=null)\r
213         cookies = Arrays.asList(request.getCookies());\r
214       else\r
215         cookies = new Vector();\r
216     }\r
217 \r
218     if (anIsComment)\r
219       logComment(address, anId , new Date(), browser);\r
220     else\r
221       logArticle(address, anId , new Date(), browser);\r
222 \r
223     return checkCookie(cookies) || checkIpFilter(address);\r
224   }\r
225 \r
226   public void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) {\r
227     try {\r
228       long time = System.currentTimeMillis();\r
229 \r
230       MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleCommentOperationForName(commentBlockAction);\r
231 \r
232       if (checkRequest(aRequest, aResponse, aComment.getId(), true) || checkRegExpFilter(aComment)) {\r
233         logger.debug("performing operation " + operation.getName());\r
234         operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("comment", aComment));\r
235         setCookie(aResponse);\r
236       }\r
237 \r
238       logger.info("checkComment: " + (System.currentTimeMillis()-time) + "ms");\r
239     }\r
240     catch (Throwable t) {\r
241       t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));\r
242       logger.error("Abuse.checkComment: " + t.toString());\r
243     }\r
244   }\r
245 \r
246   public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {\r
247     try {\r
248       long time = System.currentTimeMillis();\r
249 \r
250       MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleArticleOperationForName(articleBlockAction);\r
251 \r
252       if (checkRequest(aRequest, aResponse, anArticle.getId(), false) || checkRegExpFilter(anArticle)) {\r
253         logger.debug("performing operation " + operation.getName());\r
254         operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("content", anArticle));\r
255         setCookie(aResponse);\r
256       }\r
257 \r
258       logger.info("checkArticle: " + (System.currentTimeMillis()-time) + "ms");\r
259     }\r
260     catch (Throwable t) {\r
261       t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));\r
262       logger.error("Abuse.checkArticle: " + t.toString());\r
263     }\r
264   }\r
265 \r
266   public boolean getLogEnabled() {\r
267     return logEnabled;\r
268   }\r
269 \r
270   public void setLogEnabled(boolean anEnabled) {\r
271     if (!configuration.getString("Abuse.DisallowIPLogging", "0").equals("1"))\r
272       logEnabled = anEnabled;\r
273     truncateLog();\r
274   }\r
275 \r
276   public int getLogSize() {\r
277     return logSize;\r
278   }\r
279 \r
280   public void setLogSize(int aSize) {\r
281     logSize = aSize;\r
282     truncateLog();\r
283   }\r
284 \r
285   public boolean getOpenPostingDisabled() {\r
286     return openPostingDisabled;\r
287   }\r
288 \r
289   public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {\r
290     openPostingDisabled = anOpenPostingDisabled;\r
291   }\r
292 \r
293   public boolean getOpenPostingPassword() {\r
294     return openPostingPassword;\r
295   }\r
296 \r
297   public void setOpenPostingPassword(boolean anOpenPostingPassword) {\r
298     openPostingPassword = anOpenPostingPassword;\r
299   }\r
300 \r
301   public boolean getCookieOnBlock() {\r
302     return cookieOnBlock;\r
303   }\r
304 \r
305   public void setCookieOnBlock(boolean aCookieOnBlock) {\r
306     cookieOnBlock = aCookieOnBlock;\r
307   }\r
308 \r
309   public String getArticleBlockAction() {\r
310     return articleBlockAction;\r
311   }\r
312 \r
313   public void setArticleBlockAction(String anAction) {\r
314     articleBlockAction = anAction;\r
315   }\r
316 \r
317   public String getCommentBlockAction() {\r
318     return commentBlockAction;\r
319   }\r
320 \r
321   public void setCommentBlockAction(String anAction) {\r
322     commentBlockAction = anAction;\r
323   }\r
324 \r
325 \r
326   public List getLog() {\r
327     synchronized(log) {\r
328       try {\r
329         List result = new Vector();\r
330 \r
331         Iterator i = log.iterator();\r
332         while (i.hasNext()) {\r
333           LogEntry logEntry = (LogEntry) i.next();\r
334           Map entry = new HashMap();\r
335 \r
336           entry.put("ip", logEntry.getIpNumber());\r
337           entry.put("id", logEntry.getId());\r
338           entry.put("timestamp", new GeneratorFormatAdapters.DateFormatAdapter(logEntry.getTimeStamp(), MirPropertiesConfiguration.instance().getString("Mir.DefaultTimezone")));\r
339           if (logEntry.getIsArticle())\r
340             entry.put("type", "content");\r
341           else\r
342             entry.put("type", "comment");\r
343           entry.put("browser", logEntry.getBrowserString());\r
344 \r
345           result.add(entry);\r
346         }\r
347 \r
348         return result;\r
349       }\r
350       catch (Throwable t) {\r
351         throw new RuntimeException(t.toString());\r
352       }\r
353     }\r
354   }\r
355 \r
356   public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser) {\r
357     appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false));\r
358   }\r
359 \r
360   public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser) {\r
361     appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true));\r
362   }\r
363 \r
364   public void load() {\r
365     try {\r
366       ExtendedProperties configuration = new ExtendedProperties();\r
367 \r
368       try {\r
369         configuration = new ExtendedProperties(configFile);\r
370       }\r
371       catch (FileNotFoundException e) {\r
372       }\r
373 \r
374       getFilterConfig(filters, "abuse.filter", configuration);\r
375 \r
376       setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));\r
377       setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));\r
378       setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));\r
379       setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));\r
380       setLogSize(configuration.getInt("abuse.logSize", 10));\r
381       setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));\r
382       setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));\r
383     }\r
384     catch (Throwable t) {\r
385       throw new RuntimeException(t.toString());\r
386     }\r
387   }\r
388   public void save() {\r
389     try {\r
390       ExtendedProperties configuration = new ExtendedProperties();\r
391 \r
392       setFilterConfig(filters, "abuse.filter", configuration);\r
393 \r
394       configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled()?"1":"0");\r
395       configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword()?"1":"0");\r
396       configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock()?"1":"0");\r
397       configuration.addProperty("abuse.logEnabled", getLogEnabled()?"1":"0");\r
398       configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));\r
399       configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());\r
400       configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());\r
401 \r
402       configuration.save(new FileOutputStream(new File(configFile)), "Anti abuse configuration");\r
403     }\r
404     catch (Throwable t) {\r
405       throw new RuntimeException(t.toString());\r
406     }\r
407   }\r
408 \r
409   public List getFilterTypes() {\r
410     List result = new Vector();\r
411 \r
412     Map entry = new HashMap();\r
413     entry.put("resource", "ip");\r
414     entry.put("id", IP_FILTER_TYPE);\r
415     result.add(entry);\r
416 \r
417     entry = new HashMap();\r
418     entry.put("resource", "regexp");\r
419     entry.put("id", REGEXP_FILTER_TYPE);\r
420     result.add(entry);\r
421 \r
422     return result;\r
423   }\r
424 \r
425   public List getArticleActions() {\r
426     try {\r
427       List result = new Vector();\r
428 \r
429       Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();\r
430       while (i.hasNext()) {\r
431         MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =\r
432             (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();\r
433 \r
434         Map action = new HashMap();\r
435         action.put("resource", operation.getName());\r
436         action.put("identifier", operation.getName());\r
437 \r
438         result.add(action);\r
439       }\r
440 \r
441       return result;\r
442     }\r
443     catch (Throwable t) {\r
444       throw new RuntimeException("can't get article actions");\r
445     }\r
446   }\r
447 \r
448   public List getCommentActions() {\r
449     try {\r
450       List result = new Vector();\r
451 \r
452       Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();\r
453       while (i.hasNext()) {\r
454         MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =\r
455             (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();\r
456 \r
457         Map action = new HashMap();\r
458         action.put("resource", operation.getName());\r
459         action.put("identifier", operation.getName());\r
460 \r
461         result.add(action);\r
462       }\r
463 \r
464       return result;\r
465     }\r
466     catch (Throwable t) {\r
467       throw new RuntimeException("can't get comment actions");\r
468     }\r
469   }\r
470 \r
471   public List getFilters() {\r
472     return getFiltersAsMaps(filters);\r
473   }\r
474 \r
475   public void addFilter(String aType, String anExpression) {\r
476     addFilter(filters, aType, anExpression);\r
477   }\r
478 \r
479   public void setFilter(String anIdentifier, String aType, String anExpression) {\r
480     setFilter(filters, anIdentifier, aType, anExpression);\r
481   }\r
482 \r
483   public void deleteFilter(String anIdentifier) {\r
484     deleteFilter(filters, anIdentifier);\r
485   }\r
486 \r
487   public void validateIpFilter(String anIdentifier, String anArticleAction, String aCommentAction) throws Exception {\r
488   }\r
489 \r
490   private List getFiltersAsMaps(List aFilters) {\r
491     synchronized(aFilters) {\r
492       List result = new Vector();\r
493 \r
494       Iterator i = aFilters.iterator();\r
495       while (i.hasNext()) {\r
496         Filter filter = (Filter) i.next();\r
497         Map map = new HashMap();\r
498 \r
499         map.put("id", filter.getId());\r
500         map.put("expression", filter.getExpression());\r
501         map.put("type", filter.getType());\r
502 \r
503         result.add(map);\r
504       }\r
505       return result;\r
506     }\r
507   }\r
508 \r
509   private void addFilter(List aFilters, String aType, String anExpression) {\r
510     Filter filter = new Filter();\r
511 \r
512     filter.setId(generateId());\r
513     filter.setExpression(anExpression);\r
514     filter.setType(aType);\r
515 \r
516     synchronized (aFilters) {\r
517       aFilters.add(filter);\r
518     }\r
519   }\r
520 \r
521   private void setFilter(List aFilters, String anIdentifier, String aType, String anExpression) {\r
522     synchronized (aFilters) {\r
523       Filter filter = findFilter(aFilters, anIdentifier);\r
524 \r
525       if (filter!=null) {\r
526         filter.setExpression(anExpression);\r
527         filter.setType(aType);\r
528       }\r
529     }\r
530   }\r
531 \r
532   private Filter findFilter(List aFilters, String anIdentifier) {\r
533     synchronized (aFilters) {\r
534       Iterator i = aFilters.iterator();\r
535       while (i.hasNext()) {\r
536         Filter filter = (Filter) i.next();\r
537 \r
538         if (filter.getId().equals(anIdentifier)) {\r
539           return filter;\r
540         }\r
541       }\r
542     }\r
543 \r
544     return null;\r
545   }\r
546 \r
547   private void deleteFilter(List aFilters, String anIdentifier) {\r
548     synchronized (aFilters) {\r
549       Filter filter = findFilter(aFilters, anIdentifier);\r
550 \r
551       if (filter!=null) {\r
552         aFilters.remove(filter);\r
553       }\r
554     }\r
555   }\r
556 \r
557   private String generateId() {\r
558     synchronized(this) {\r
559       maxIdentifier = maxIdentifier+1;\r
560 \r
561       return Integer.toString(maxIdentifier);\r
562     }\r
563   }\r
564 \r
565   private static class Filter {\r
566     private String identifier;\r
567     private String expression;\r
568     private String type;\r
569 \r
570     public Filter() {\r
571       expression="";\r
572       type="";\r
573       identifier="";\r
574     }\r
575 \r
576     public String getId() {\r
577       return identifier;\r
578     }\r
579 \r
580     public void setId(String anId) {\r
581       identifier = anId;\r
582     }\r
583 \r
584     public String getExpression() {\r
585       return expression;\r
586     }\r
587 \r
588     public void setExpression(String anExpression) {\r
589       expression = anExpression;\r
590     }\r
591 \r
592     public String getType() {\r
593       return type;\r
594     }\r
595 \r
596     public void setType(String aType) {\r
597       type = aType;\r
598     }\r
599   }\r
600 \r
601   private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {\r
602     synchronized(aFilters) {\r
603       Iterator i = aFilters.iterator();\r
604 \r
605       while (i.hasNext()) {\r
606         Filter filter = (Filter) i.next();\r
607 \r
608         aConfiguration.addProperty(aConfigKey, filter.getType()+":"+filter.getExpression());\r
609       }\r
610     }\r
611   }\r
612 \r
613   private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {\r
614     synchronized(aFilters) {\r
615       aFilters.clear();\r
616 \r
617       if (aConfiguration.getStringArray(aConfigKey)!=null) {\r
618 \r
619         Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).\r
620             iterator();\r
621 \r
622         while (i.hasNext()) {\r
623           String filter = (String) i.next();\r
624           List parts = StringRoutines.separateString(filter, ":");\r
625 \r
626           if (parts.size() == 2) {\r
627             addFilter( (String) parts.get(0), (String) parts.get(1));\r
628           }\r
629         }\r
630       }\r
631     }\r
632   }\r
633 \r
634   private static class LogEntry {\r
635     private String ipNumber;\r
636     private String browserString;\r
637     private String id;\r
638     private Date timeStamp;\r
639     private boolean isArticle;\r
640 \r
641     public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {\r
642       ipNumber = anIpNumber;\r
643       browserString = aBrowserString;\r
644       id = anId;\r
645       isArticle = anIsArticle;\r
646       timeStamp=aTimeStamp;\r
647     }\r
648 \r
649     public String getIpNumber() {\r
650       return ipNumber;\r
651     }\r
652 \r
653     public String getBrowserString() {\r
654       return browserString;\r
655     }\r
656 \r
657     public String getId() {\r
658       return id;\r
659     }\r
660 \r
661     public Date getTimeStamp() {\r
662       return timeStamp;\r
663     }\r
664 \r
665     public boolean getIsArticle() {\r
666       return isArticle;\r
667     }\r
668   }\r
669 \r
670   private void truncateLog() {\r
671     synchronized(log) {\r
672       if (!logEnabled)\r
673         log.clear();\r
674       else {\r
675         while (log.size()>0 && log.size()>logSize) {\r
676           log.remove(0);\r
677         }\r
678       }\r
679     }\r
680   };\r
681 \r
682   private void appendLog(LogEntry anEntry) {\r
683     synchronized (log) {\r
684       if (logEnabled) {\r
685         log.add(anEntry);\r
686         truncateLog();\r
687       }\r
688     }\r
689   }\r
690 \r
691   public void logAdminUsage(EntityUsers aUser, String aDescription) {\r
692     try {\r
693       String user = "unknown (" + aUser.toString() +")";\r
694       if (user!=null)\r
695         user = aUser.getValue("login");\r
696       adminUsageLogger.info(user + ": " + aDescription);\r
697     }\r
698     catch (Throwable t) {\r
699       logger.error("Error while logging admin usage ("+aUser.toString()+", "+aDescription+"): " +t.toString());\r
700     }\r
701   }\r
702 }