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