2 * Copyright (C) 2001, 2002 The Mir-coders group
4 * This file is part of Mir.
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.
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.
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
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.
31 package mircoders.global;
33 import java.io.BufferedOutputStream;
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;
44 import java.util.Random;
46 import javax.servlet.http.Cookie;
47 import javax.servlet.http.HttpServletResponse;
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;
63 import org.apache.commons.collections.ExtendedProperties;
67 private LoggerWrapper logger;
69 private boolean logEnabled;
70 private boolean openPostingDisabled;
71 private boolean openPostingPassword;
72 private boolean cookieOnBlock;
73 private String articleBlockAction;
74 private String commentBlockAction;
76 private File configFile = MirGlobal.config().getFile("Abuse.Config");
77 private FilterEngine filterEngine;
79 private MirPropertiesConfiguration configuration;
81 private static String cookieName = MirGlobal.config().getString("Abuse.CookieName");
82 private static int cookieMaxAge = 60 * 60 * MirGlobal.config().getInt("Abuse.CookieMaxAge");
84 public Abuse(EntityAdapterModel aModel) {
85 logger = new LoggerWrapper("Global.Abuse");
86 filterEngine = new FilterEngine(aModel);
88 log = new ArrayList();
91 configuration = MirPropertiesConfiguration.instance();
94 throw new RuntimeException("Can't get configuration: " + e.getMessage());
99 articleBlockAction = "";
100 commentBlockAction = "";
101 openPostingPassword = false;
102 openPostingDisabled = false;
103 cookieOnBlock = false;
108 public FilterEngine getFilterEngine() {
112 private void setCookie(HttpServletResponse aResponse) {
113 Random random = new Random();
115 Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000)));
116 cookie.setMaxAge(cookieMaxAge);
119 if (aResponse != null)
120 aResponse.addCookie(cookie);
123 private boolean checkCookie(List aCookies) {
124 if (getCookieOnBlock()) {
125 Iterator i = aCookies.iterator();
127 while (i.hasNext()) {
128 Cookie cookie = (Cookie) i.next();
130 if (cookie.getName().equals(cookieName)) {
131 logger.debug("cookie match");
140 public void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) {
142 long time = System.currentTimeMillis();
144 FilterEngine.Filter matchingFilter = filterEngine.testPosting(aComment, aRequest);
146 if (matchingFilter != null) {
147 logger.debug("Match for " + matchingFilter.getTag());
148 matchingFilter.updateLastHit(new GregorianCalendar().getTime());
150 StringBuffer line = new StringBuffer();
152 line.append(DateTimeFunctions.advancedDateFormat(
153 configuration.getString("Mir.DefaultDateTimeFormat"),
154 (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));
157 line.append(matchingFilter.getTag());
158 EntityUtility.appendLineToField(aComment, "comment", line.toString());
160 MirGlobal.performCommentOperation(null, aComment, matchingFilter.getCommentAction());
161 setCookie(aResponse);
163 logComment(aComment, aRequest, matchingFilter.getTag());
166 logComment(aComment, aRequest);
169 logger.debug("checkComment: " + (System.currentTimeMillis() - time) + "ms");
171 catch (Throwable t) {
172 logger.error("Exception thrown while checking comment", t);
176 public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {
178 long time = System.currentTimeMillis();
180 FilterEngine.Filter matchingFilter = filterEngine.testPosting(anArticle, aRequest);
182 if (matchingFilter != null) {
183 logger.debug("Match for " + matchingFilter.getTag());
184 // matchingFilter.updateLastHit(new GregorianCalendar().getTime());
186 StringBuffer line = new StringBuffer();
188 line.append(DateTimeFunctions.advancedDateFormat(
189 configuration.getString("Mir.DefaultDateTimeFormat"),
190 (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));
193 line.append(matchingFilter.getTag());
194 EntityUtility.appendLineToField(anArticle, "comment", line.toString());
196 MirGlobal.performArticleOperation(null, anArticle, matchingFilter.getArticleAction());
197 setCookie(aResponse);
199 logArticle(anArticle, aRequest, matchingFilter.getTag());
202 logArticle(anArticle, aRequest);
205 logger.info("checkArticle: " + (System.currentTimeMillis() - time) + "ms");
207 catch (Throwable t) {
208 logger.error("Exception thrown while checking article", t);
212 public boolean getLogEnabled() {
216 public void setLogEnabled(boolean anEnabled) {
217 if (!configuration.getString("Abuse.DisallowIPLogging", "0").equals("1"))
218 logEnabled = anEnabled;
222 public int getLogSize() {
226 public void setLogSize(int aSize) {
231 public boolean getOpenPostingDisabled() {
232 return openPostingDisabled;
235 public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {
236 openPostingDisabled = anOpenPostingDisabled;
239 public boolean getOpenPostingPassword() {
240 return openPostingPassword;
243 public void setOpenPostingPassword(boolean anOpenPostingPassword) {
244 openPostingPassword = anOpenPostingPassword;
247 public boolean getCookieOnBlock() {
248 return cookieOnBlock;
251 public void setCookieOnBlock(boolean aCookieOnBlock) {
252 cookieOnBlock = aCookieOnBlock;
255 public String getArticleBlockAction() {
256 return articleBlockAction;
259 public void setArticleBlockAction(String anAction) {
260 articleBlockAction = anAction;
263 public String getCommentBlockAction() {
264 return commentBlockAction;
267 public void setCommentBlockAction(String anAction) {
268 commentBlockAction = anAction;
271 public List getLog() {
274 List result = new ArrayList();
276 Iterator i = log.iterator();
277 while (i.hasNext()) {
278 LogEntry logEntry = (LogEntry) i.next();
279 Map entry = new HashMap();
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");
287 entry.put("type", "comment");
288 entry.put("browser", logEntry.getBrowserString());
289 entry.put("filtertag", logEntry.getMatchingFilterTag());
296 catch (Throwable t) {
297 throw new RuntimeException(t.toString());
302 public void logComment(Entity aComment, Request aRequest) {
303 logComment(aComment, aRequest, null);
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");
311 logComment(ipAddress, id, new Date(), browser, aMatchingFilterTag);
314 public void logArticle(Entity anArticle, Request aRequest) {
315 logArticle(anArticle, aRequest, null);
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");
323 logArticle(ipAddress, id, new Date(), browser, aMatchingFilterTag);
326 public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser, String aMatchingFilterTag) {
327 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false, aMatchingFilterTag));
330 public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser, String aMatchingFilterTag) {
331 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true, aMatchingFilterTag));
334 public synchronized void load() {
336 ExtendedProperties configuration = new ExtendedProperties();
339 configuration = new ExtendedProperties(configFile.getAbsolutePath());
341 catch (FileNotFoundException e) {
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", ""));
352 catch (Throwable t) {
353 throw new RuntimeException(t.toString());
357 public synchronized void save() {
359 ExtendedProperties configuration = new ExtendedProperties();
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());
369 configuration.save(new BufferedOutputStream(new FileOutputStream(configFile),8192), "Anti abuse configuration");
371 catch (Throwable t) {
372 throw new RuntimeException(t.toString());
376 public List getArticleActions() {
378 List result = new ArrayList();
380 Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();
381 while (i.hasNext()) {
382 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
383 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
385 Map action = new HashMap();
386 action.put("resource", operation.getName());
387 action.put("identifier", operation.getName());
394 catch (Throwable t) {
395 throw new RuntimeException("can't get article actions");
399 public List getCommentActions() {
401 List result = new ArrayList();
403 Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();
404 while (i.hasNext()) {
405 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
406 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
408 Map action = new HashMap();
409 action.put("resource", operation.getName());
410 action.put("identifier", operation.getName());
417 catch (Throwable t) {
418 throw new RuntimeException("can't get comment actions");
422 private String escapeConfigListEntry(String aFilterPart) {
423 return StringRoutines.replaceStringCharacters(aFilterPart,
424 new char[] {'\\', ':'},
425 new String[] {"\\\\", "\\:"});
428 private String escapeFilterPart(String aFilterPart) {
429 return StringRoutines.replaceStringCharacters(aFilterPart,
430 new char[] {'\\', '\n', '\r', '\t', ' '},
431 new String[] {"\\\\", "\\n", "\\r", "\\t", "\\ "});
434 private String deescapeFilterPart(String aFilterPart) {
435 return StringRoutines.replaceEscapedStringCharacters(aFilterPart,
437 new char[] {'\\', ':', 'n', 'r', 't', ' '},
438 new String[] {"\\", ":", "\n", "\r", "\t", " "});
441 private static class LogEntry {
442 private String ipNumber;
443 private String browserString;
445 private Date timeStamp;
446 private boolean isArticle;
447 private String matchingFilterTag;
449 public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle, String aMatchingFilterTag) {
450 ipNumber = anIpNumber;
451 browserString = aBrowserString;
453 isArticle = anIsArticle;
454 timeStamp = aTimeStamp;
455 matchingFilterTag = aMatchingFilterTag;
458 public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {
459 this(aTimeStamp, anIpNumber, aBrowserString, anId, anIsArticle, null);
462 public String getIpNumber() {
466 public String getBrowserString() {
467 return browserString;
470 public String getId() {
474 public String getMatchingFilterTag() {
475 return matchingFilterTag;
478 public Date getTimeStamp() {
482 public boolean getIsArticle() {
487 private void truncateLog() {
492 while (log.size() > 0 && log.size() > logSize) {
493 log.remove(log.size()-1);
499 private void appendLog(LogEntry anEntry) {