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 mir.config.MirPropertiesConfiguration;
34 import mir.entity.Entity;
35 import mir.entity.adapter.EntityAdapterModel;
36 import mir.log.LoggerWrapper;
37 import mir.session.Request;
38 import mir.util.DateTimeFunctions;
39 import mir.util.EntityUtility;
40 import mir.util.GeneratorFormatAdapters;
41 import mir.util.StringRoutines;
42 import mircoders.abuse.FilterEngine;
43 import mircoders.entity.EntityComment;
44 import mircoders.entity.EntityContent;
45 import mircoders.localizer.MirAdminInterfaceLocalizer;
46 import org.apache.commons.collections.ExtendedProperties;
48 import javax.servlet.http.Cookie;
49 import javax.servlet.http.HttpServletResponse;
50 import java.io.BufferedOutputStream;
52 import java.io.FileNotFoundException;
53 import java.io.FileOutputStream;
58 private LoggerWrapper logger;
60 private boolean logEnabled;
61 private boolean openPostingDisabled;
62 private boolean openPostingPassword;
63 private boolean cookieOnBlock;
64 private String articleBlockAction;
65 private String commentBlockAction;
67 private File configFile = MirGlobal.config().getFile("Abuse.Config");
68 private FilterEngine filterEngine;
70 private MirPropertiesConfiguration configuration;
72 private static String cookieName = MirGlobal.config().getString("Abuse.CookieName");
73 private static int cookieMaxAge = 60 * 60 * MirGlobal.config().getInt("Abuse.CookieMaxAge");
75 public Abuse(EntityAdapterModel aModel) {
76 logger = new LoggerWrapper("Global.Abuse");
77 filterEngine = new FilterEngine(aModel);
79 log = new ArrayList();
82 configuration = MirPropertiesConfiguration.instance();
85 throw new RuntimeException("Can't get configuration: " + e.getMessage());
90 articleBlockAction = "";
91 commentBlockAction = "";
92 openPostingPassword = false;
93 openPostingDisabled = false;
94 cookieOnBlock = false;
99 public FilterEngine getFilterEngine() {
103 private void setCookie(HttpServletResponse aResponse) {
104 Random random = new Random();
106 Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000)));
107 cookie.setMaxAge(cookieMaxAge);
110 if (aResponse != null)
111 aResponse.addCookie(cookie);
114 private boolean checkCookie(List aCookies) {
115 if (getCookieOnBlock()) {
116 Iterator i = aCookies.iterator();
118 while (i.hasNext()) {
119 Cookie cookie = (Cookie) i.next();
121 if (cookie.getName().equals(cookieName)) {
122 logger.debug("cookie match");
131 public void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) {
133 long time = System.currentTimeMillis();
135 FilterEngine.Filter matchingFilter = filterEngine.testPosting(aComment, aRequest);
137 if (matchingFilter != null) {
138 logger.debug("Match for " + matchingFilter.getTag());
139 matchingFilter.updateLastHit(new GregorianCalendar().getTime());
141 StringBuffer line = new StringBuffer();
143 line.append(DateTimeFunctions.advancedDateFormat(
144 configuration.getString("Mir.DefaultDateTimeFormat"),
145 (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));
148 line.append(matchingFilter.getTag());
149 EntityUtility.appendLineToField(aComment, "comment", line.toString());
151 MirGlobal.performCommentOperation(null, aComment, matchingFilter.getCommentAction());
152 setCookie(aResponse);
154 logComment(aComment, aRequest, matchingFilter.getTag());
157 logComment(aComment, aRequest);
160 logger.debug("checkComment: " + (System.currentTimeMillis() - time) + "ms");
162 catch (Throwable t) {
163 logger.error("Exception thrown while checking comment", t);
167 public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {
169 long time = System.currentTimeMillis();
171 FilterEngine.Filter matchingFilter = filterEngine.testPosting(anArticle, aRequest);
173 if (matchingFilter != null) {
174 logger.debug("Match for " + matchingFilter.getTag());
175 // matchingFilter.updateLastHit(new GregorianCalendar().getTime());
177 StringBuffer line = new StringBuffer();
179 line.append(DateTimeFunctions.advancedDateFormat(
180 configuration.getString("Mir.DefaultDateTimeFormat"),
181 (new GregorianCalendar()).getTime(), configuration.getString("Mir.DefaultTimezone")));
184 line.append(matchingFilter.getTag());
185 EntityUtility.appendLineToField(anArticle, "comment", line.toString());
187 MirGlobal.performArticleOperation(null, anArticle, matchingFilter.getArticleAction());
188 setCookie(aResponse);
190 logArticle(anArticle, aRequest, matchingFilter.getTag());
193 logArticle(anArticle, aRequest);
196 logger.info("checkArticle: " + (System.currentTimeMillis() - time) + "ms");
198 catch (Throwable t) {
199 logger.error("Exception thrown while checking article", t);
203 public boolean getLogEnabled() {
207 public void setLogEnabled(boolean anEnabled) {
208 if (!configuration.getString("Abuse.DisallowIPLogging", "0").equals("1"))
209 logEnabled = anEnabled;
213 public int getLogSize() {
217 public void setLogSize(int aSize) {
222 public boolean getOpenPostingDisabled() {
223 return openPostingDisabled;
226 public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {
227 openPostingDisabled = anOpenPostingDisabled;
230 public boolean getOpenPostingPassword() {
231 return openPostingPassword;
234 public void setOpenPostingPassword(boolean anOpenPostingPassword) {
235 openPostingPassword = anOpenPostingPassword;
238 public boolean getCookieOnBlock() {
239 return cookieOnBlock;
242 public void setCookieOnBlock(boolean aCookieOnBlock) {
243 cookieOnBlock = aCookieOnBlock;
246 public String getArticleBlockAction() {
247 return articleBlockAction;
250 public void setArticleBlockAction(String anAction) {
251 articleBlockAction = anAction;
254 public String getCommentBlockAction() {
255 return commentBlockAction;
258 public void setCommentBlockAction(String anAction) {
259 commentBlockAction = anAction;
262 public List getLog() {
265 List result = new ArrayList();
267 Iterator i = log.iterator();
268 while (i.hasNext()) {
269 LogEntry logEntry = (LogEntry) i.next();
270 Map entry = new HashMap();
272 entry.put("ip", logEntry.getIpNumber());
273 entry.put("id", logEntry.getId());
274 entry.put("timestamp", new GeneratorFormatAdapters.DateFormatAdapter(logEntry.getTimeStamp(), MirPropertiesConfiguration.instance().getString("Mir.DefaultTimezone")));
275 if (logEntry.getIsArticle())
276 entry.put("type", "content");
278 entry.put("type", "comment");
279 entry.put("browser", logEntry.getBrowserString());
280 entry.put("filtertag", logEntry.getMatchingFilterTag());
287 catch (Throwable t) {
288 throw new RuntimeException(t.toString());
293 public void logComment(Entity aComment, Request aRequest) {
294 logComment(aComment, aRequest, null);
297 public void logComment(Entity aComment, Request aRequest, String aMatchingFilterTag) {
298 String ipAddress = aRequest.getHeader("ip");
299 String id = aComment.getId();
300 String browser = aRequest.getHeader("User-Agent");
302 logComment(ipAddress, id, new Date(), browser, aMatchingFilterTag);
305 public void logArticle(Entity anArticle, Request aRequest) {
306 logArticle(anArticle, aRequest, null);
309 public void logArticle(Entity anArticle, Request aRequest, String aMatchingFilterTag) {
310 String ipAddress = aRequest.getHeader("ip");
311 String id = anArticle.getId();
312 String browser = aRequest.getHeader("User-Agent");
314 logArticle(ipAddress, id, new Date(), browser, aMatchingFilterTag);
317 public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser, String aMatchingFilterTag) {
318 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false, aMatchingFilterTag));
321 public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser, String aMatchingFilterTag) {
322 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true, aMatchingFilterTag));
325 public synchronized void load() {
327 ExtendedProperties configuration = new ExtendedProperties();
330 configuration = new ExtendedProperties(configFile.getAbsolutePath());
332 catch (FileNotFoundException e) {
335 setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));
336 setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));
337 setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));
338 setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));
339 setLogSize(configuration.getInt("abuse.logSize", 10));
340 setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));
341 setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));
343 catch (Throwable t) {
344 throw new RuntimeException(t.toString());
348 public synchronized void save() {
350 ExtendedProperties configuration = new ExtendedProperties();
352 configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled() ? "1" : "0");
353 configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword() ? "1" : "0");
354 configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock() ? "1" : "0");
355 configuration.addProperty("abuse.logEnabled", getLogEnabled() ? "1" : "0");
356 configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));
357 configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());
358 configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());
360 configuration.save(new BufferedOutputStream(new FileOutputStream(configFile),8192), "Anti abuse configuration");
362 catch (Throwable t) {
363 throw new RuntimeException(t.toString());
367 public List getArticleActions() {
369 List result = new ArrayList();
371 Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();
372 while (i.hasNext()) {
373 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
374 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
376 Map action = new HashMap();
377 action.put("resource", operation.getName());
378 action.put("identifier", operation.getName());
385 catch (Throwable t) {
386 throw new RuntimeException("can't get article actions");
390 public List getCommentActions() {
392 List result = new ArrayList();
394 Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();
395 while (i.hasNext()) {
396 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
397 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
399 Map action = new HashMap();
400 action.put("resource", operation.getName());
401 action.put("identifier", operation.getName());
408 catch (Throwable t) {
409 throw new RuntimeException("can't get comment actions");
413 private String escapeConfigListEntry(String aFilterPart) {
414 return StringRoutines.replaceStringCharacters(aFilterPart,
415 new char[] {'\\', ':'},
416 new String[] {"\\\\", "\\:"});
419 private String escapeFilterPart(String aFilterPart) {
420 return StringRoutines.replaceStringCharacters(aFilterPart,
421 new char[] {'\\', '\n', '\r', '\t', ' '},
422 new String[] {"\\\\", "\\n", "\\r", "\\t", "\\ "});
425 private String deescapeFilterPart(String aFilterPart) {
426 return StringRoutines.replaceEscapedStringCharacters(aFilterPart,
428 new char[] {'\\', ':', 'n', 'r', 't', ' '},
429 new String[] {"\\", ":", "\n", "\r", "\t", " "});
432 private static class LogEntry {
433 private String ipNumber;
434 private String browserString;
436 private Date timeStamp;
437 private boolean isArticle;
438 private String matchingFilterTag;
440 public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle, String aMatchingFilterTag) {
441 ipNumber = anIpNumber;
442 browserString = aBrowserString;
444 isArticle = anIsArticle;
445 timeStamp = aTimeStamp;
446 matchingFilterTag = aMatchingFilterTag;
449 public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {
450 this(aTimeStamp, anIpNumber, aBrowserString, anId, anIsArticle, null);
453 public String getIpNumber() {
457 public String getBrowserString() {
458 return browserString;
461 public String getId() {
465 public String getMatchingFilterTag() {
466 return matchingFilterTag;
469 public Date getTimeStamp() {
473 public boolean getIsArticle() {
478 private void truncateLog() {
483 while (log.size() > 0 && log.size() > logSize) {
484 log.remove(log.size()-1);
490 private void appendLog(LogEntry anEntry) {