some code cleanup. removed unnecessary semikolons, unused vars, etc.
[mir.git] / source / mircoders / global / Abuse.java
1 /*
2  * Copyright (C) 2001, 2002 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with  any library licensed under the Apache Software License,
22  * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23  * (or with modified versions of the above that use the same license as the above),
24  * and distribute linked combinations including the two.  You must obey the
25  * GNU General Public License in all respects for all of the code used other than
26  * the above mentioned libraries.  If you modify this file, you may extend this
27  * exception to your version of the file, but you are not obligated to do so.
28  * If you do not wish to do so, delete this exception statement from your version.
29  */
30
31 package mircoders.global;
32
33 import java.io.BufferedOutputStream;
34 import java.io.File;
35 import java.io.FileNotFoundException;
36 import java.io.FileOutputStream;
37 import java.util.ArrayList;
38 import java.util.Date;
39 import java.util.GregorianCalendar;
40 import java.util.HashMap;
41 import java.util.Iterator;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Random;
45
46 import javax.servlet.http.Cookie;
47 import javax.servlet.http.HttpServletResponse;
48
49 import mir.config.MirPropertiesConfiguration;
50 import mir.entity.Entity;
51 import mir.entity.adapter.EntityAdapterModel;
52 import mir.log.LoggerWrapper;
53 import mir.session.Request;
54 import mir.util.DateTimeFunctions;
55 import mir.util.EntityUtility;
56 import mir.util.GeneratorFormatAdapters;
57 import mir.util.StringRoutines;
58 import mircoders.abuse.FilterEngine;
59 import mircoders.entity.EntityComment;
60 import mircoders.entity.EntityContent;
61 import mircoders.localizer.MirAdminInterfaceLocalizer;
62
63 import org.apache.commons.collections.ExtendedProperties;
64
65
66 public class Abuse {
67   private LoggerWrapper logger;
68   private int logSize;
69   private boolean logEnabled;
70   private boolean openPostingDisabled;
71   private boolean openPostingPassword;
72   private boolean cookieOnBlock;
73   private String articleBlockAction;
74   private String commentBlockAction;
75   private List log;
76   private File configFile = MirGlobal.config().getFile("Abuse.Config");
77   private FilterEngine filterEngine;
78
79   private MirPropertiesConfiguration configuration;
80
81   private static String cookieName = MirGlobal.config().getString("Abuse.CookieName");
82   private static int cookieMaxAge = 60 * 60 * MirGlobal.config().getInt("Abuse.CookieMaxAge");
83
84   public Abuse(EntityAdapterModel aModel) {
85     logger = new LoggerWrapper("Global.Abuse");
86     filterEngine = new FilterEngine(aModel);
87
88     log = new ArrayList();
89
90     try {
91       configuration = MirPropertiesConfiguration.instance();
92     }
93     catch (Throwable e) {
94       throw new RuntimeException("Can't get configuration: " + e.getMessage());
95     }
96
97     logSize = 100;
98     logEnabled = false;
99     articleBlockAction = "";
100     commentBlockAction = "";
101     openPostingPassword = false;
102     openPostingDisabled = false;
103     cookieOnBlock = false;
104
105     load();
106   }
107
108   public FilterEngine getFilterEngine() {
109     return filterEngine;
110   }
111
112   private void setCookie(HttpServletResponse aResponse) {
113     Random random = new Random();
114
115     Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000)));
116     cookie.setMaxAge(cookieMaxAge);
117     cookie.setPath("/");
118
119     if (aResponse != null)
120       aResponse.addCookie(cookie);
121   }
122
123   private boolean checkCookie(List aCookies) {
124     if (getCookieOnBlock()) {
125       Iterator i = aCookies.iterator();
126
127       while (i.hasNext()) {
128         Cookie cookie = (Cookie) i.next();
129
130         if (cookie.getName().equals(cookieName)) {
131           logger.debug("cookie match");
132           return true;
133         }
134       }
135     }
136
137     return false;
138   }
139
140   public void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) {
141     try {
142       long time = System.currentTimeMillis();
143
144       FilterEngine.Filter matchingFilter = filterEngine.testPosting(aComment, aRequest);
145
146       if (matchingFilter != null) {
147         logger.debug("Match for " + matchingFilter.getTag());
148         matchingFilter.updateLastHit(new GregorianCalendar().getTime());
149
150         StringBuffer line = new StringBuffer();
151
152         line.append(DateTimeFunctions.advancedDateFormat(
153             configuration.getString("Mir.DefaultDateTimeFormat"),
154             (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));
155
156         line.append(" ");
157         line.append(matchingFilter.getTag());
158         EntityUtility.appendLineToField(aComment, "comment", line.toString());
159
160         MirGlobal.performCommentOperation(null, aComment, matchingFilter.getCommentAction());
161         setCookie(aResponse);
162         save();
163         logComment(aComment, aRequest, matchingFilter.getTag());
164       }
165       else {
166         logComment(aComment, aRequest);
167       }
168
169       logger.debug("checkComment: " + (System.currentTimeMillis() - time) + "ms");
170     }
171     catch (Throwable t) {
172       logger.error("Exception thrown while checking comment", t);
173     }
174   }
175
176   public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {
177     try {
178       long time = System.currentTimeMillis();
179
180       FilterEngine.Filter matchingFilter = filterEngine.testPosting(anArticle, aRequest);
181
182       if (matchingFilter != null) {
183         logger.debug("Match for " + matchingFilter.getTag());
184 //        matchingFilter.updateLastHit(new GregorianCalendar().getTime());
185
186         StringBuffer line = new StringBuffer();
187
188         line.append(DateTimeFunctions.advancedDateFormat(
189             configuration.getString("Mir.DefaultDateTimeFormat"),
190             (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));
191
192         line.append(" ");
193         line.append(matchingFilter.getTag());
194         EntityUtility.appendLineToField(anArticle, "comment", line.toString());
195
196         MirGlobal.performArticleOperation(null, anArticle, matchingFilter.getArticleAction());
197         setCookie(aResponse);
198         save();
199         logArticle(anArticle, aRequest, matchingFilter.getTag());
200       }
201       else {
202         logArticle(anArticle, aRequest);
203       }
204
205       logger.info("checkArticle: " + (System.currentTimeMillis() - time) + "ms");
206     }
207     catch (Throwable t) {
208       logger.error("Exception thrown while checking article", t);
209     }
210   }
211
212   public boolean getLogEnabled() {
213     return logEnabled;
214   }
215
216   public void setLogEnabled(boolean anEnabled) {
217     if (!configuration.getString("Abuse.DisallowIPLogging", "0").equals("1"))
218       logEnabled = anEnabled;
219     truncateLog();
220   }
221
222   public int getLogSize() {
223     return logSize;
224   }
225
226   public void setLogSize(int aSize) {
227     logSize = aSize;
228     truncateLog();
229   }
230
231   public boolean getOpenPostingDisabled() {
232     return openPostingDisabled;
233   }
234
235   public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {
236     openPostingDisabled = anOpenPostingDisabled;
237   }
238
239   public boolean getOpenPostingPassword() {
240     return openPostingPassword;
241   }
242
243   public void setOpenPostingPassword(boolean anOpenPostingPassword) {
244     openPostingPassword = anOpenPostingPassword;
245   }
246
247   public boolean getCookieOnBlock() {
248     return cookieOnBlock;
249   }
250
251   public void setCookieOnBlock(boolean aCookieOnBlock) {
252     cookieOnBlock = aCookieOnBlock;
253   }
254
255   public String getArticleBlockAction() {
256     return articleBlockAction;
257   }
258
259   public void setArticleBlockAction(String anAction) {
260     articleBlockAction = anAction;
261   }
262
263   public String getCommentBlockAction() {
264     return commentBlockAction;
265   }
266
267   public void setCommentBlockAction(String anAction) {
268     commentBlockAction = anAction;
269   }
270
271   public List getLog() {
272     synchronized (log) {
273       try {
274         List result = new ArrayList();
275
276         Iterator i = log.iterator();
277         while (i.hasNext()) {
278           LogEntry logEntry = (LogEntry) i.next();
279           Map entry = new HashMap();
280
281           entry.put("ip", logEntry.getIpNumber());
282           entry.put("id", logEntry.getId());
283           entry.put("timestamp", new GeneratorFormatAdapters.DateFormatAdapter(logEntry.getTimeStamp(), MirPropertiesConfiguration.instance().getString("Mir.DefaultTimezone")));
284           if (logEntry.getIsArticle())
285             entry.put("type", "content");
286           else
287             entry.put("type", "comment");
288           entry.put("browser", logEntry.getBrowserString());
289           entry.put("filtertag", logEntry.getMatchingFilterTag());
290
291           result.add(entry);
292         }
293
294         return result;
295       }
296       catch (Throwable t) {
297         throw new RuntimeException(t.toString());
298       }
299     }
300   }
301
302   public void logComment(Entity aComment, Request aRequest) {
303     logComment(aComment, aRequest, null);
304   }
305
306   public void logComment(Entity aComment, Request aRequest, String aMatchingFilterTag) {
307     String ipAddress = aRequest.getHeader("ip");
308     String id = aComment.getId();
309     String browser = aRequest.getHeader("User-Agent");
310
311     logComment(ipAddress, id, new Date(), browser, aMatchingFilterTag);
312   }
313
314   public void logArticle(Entity anArticle, Request aRequest) {
315     logArticle(anArticle, aRequest, null);
316   }
317
318   public void logArticle(Entity anArticle, Request aRequest, String aMatchingFilterTag) {
319     String ipAddress = aRequest.getHeader("ip");
320     String id = anArticle.getId();
321     String browser = aRequest.getHeader("User-Agent");
322
323     logArticle(ipAddress, id, new Date(), browser, aMatchingFilterTag);
324   }
325
326   public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser, String aMatchingFilterTag) {
327     appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false, aMatchingFilterTag));
328   }
329
330   public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser, String aMatchingFilterTag) {
331     appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true, aMatchingFilterTag));
332   }
333
334   public synchronized void load() {
335     try {
336       ExtendedProperties configuration = new ExtendedProperties();
337
338       try {
339         configuration = new ExtendedProperties(configFile.getAbsolutePath());
340       }
341       catch (FileNotFoundException e) {
342       }
343
344       setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));
345       setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));
346       setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));
347       setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));
348       setLogSize(configuration.getInt("abuse.logSize", 10));
349       setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));
350       setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));
351     }
352     catch (Throwable t) {
353       throw new RuntimeException(t.toString());
354     }
355   }
356
357   public synchronized void save() {
358     try {
359       ExtendedProperties configuration = new ExtendedProperties();
360
361       configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled() ? "1" : "0");
362       configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword() ? "1" : "0");
363       configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock() ? "1" : "0");
364       configuration.addProperty("abuse.logEnabled", getLogEnabled() ? "1" : "0");
365       configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));
366       configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());
367       configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());
368
369       configuration.save(new BufferedOutputStream(new FileOutputStream(configFile),8192), "Anti abuse configuration");
370     }
371     catch (Throwable t) {
372       throw new RuntimeException(t.toString());
373     }
374   }
375
376   public List getArticleActions() {
377     try {
378       List result = new ArrayList();
379
380       Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();
381       while (i.hasNext()) {
382         MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
383             (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
384
385         Map action = new HashMap();
386         action.put("resource", operation.getName());
387         action.put("identifier", operation.getName());
388
389         result.add(action);
390       }
391
392       return result;
393     }
394     catch (Throwable t) {
395       throw new RuntimeException("can't get article actions");
396     }
397   }
398
399   public List getCommentActions() {
400     try {
401       List result = new ArrayList();
402
403       Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();
404       while (i.hasNext()) {
405         MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
406             (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
407
408         Map action = new HashMap();
409         action.put("resource", operation.getName());
410         action.put("identifier", operation.getName());
411
412         result.add(action);
413       }
414
415       return result;
416     }
417     catch (Throwable t) {
418       throw new RuntimeException("can't get comment actions");
419     }
420   }
421
422   private String escapeConfigListEntry(String aFilterPart) {
423     return StringRoutines.replaceStringCharacters(aFilterPart,
424         new char[] {'\\', ':'},
425         new String[] {"\\\\", "\\:"});
426   }
427
428   private String escapeFilterPart(String aFilterPart) {
429     return StringRoutines.replaceStringCharacters(aFilterPart,
430         new char[] {'\\', '\n', '\r', '\t', ' '},
431         new String[] {"\\\\", "\\n", "\\r", "\\t", "\\ "});
432   }
433
434   private String deescapeFilterPart(String aFilterPart) {
435     return StringRoutines.replaceEscapedStringCharacters(aFilterPart,
436         '\\',
437         new char[] {'\\', ':', 'n', 'r', 't', ' '},
438         new String[] {"\\", ":", "\n", "\r", "\t", " "});
439   }
440
441   private static class LogEntry {
442     private String ipNumber;
443     private String browserString;
444     private String id;
445     private Date timeStamp;
446     private boolean isArticle;
447     private String matchingFilterTag;
448
449     public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle, String aMatchingFilterTag) {
450       ipNumber = anIpNumber;
451       browserString = aBrowserString;
452       id = anId;
453       isArticle = anIsArticle;
454       timeStamp = aTimeStamp;
455       matchingFilterTag = aMatchingFilterTag;
456     }
457
458     public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {
459       this(aTimeStamp, anIpNumber, aBrowserString, anId, anIsArticle, null);
460     }
461
462     public String getIpNumber() {
463       return ipNumber;
464     }
465
466     public String getBrowserString() {
467       return browserString;
468     }
469
470     public String getId() {
471       return id;
472     }
473
474     public String getMatchingFilterTag() {
475       return matchingFilterTag;
476     }
477
478     public Date getTimeStamp() {
479       return timeStamp;
480     }
481
482     public boolean getIsArticle() {
483       return isArticle;
484     }
485   }
486
487   private void truncateLog() {
488     synchronized (log) {
489       if (!logEnabled)
490         log.clear();
491       else {
492         while (log.size() > 0 && log.size() > logSize) {
493           log.remove(log.size()-1);
494         }
495       }
496     }
497   }
498
499   private void appendLog(LogEntry anEntry) {
500     synchronized (log) {
501       if (logEnabled) {
502         log.add(0, anEntry);
503         truncateLog();
504       }
505     }
506   }
507 }