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;
36 import java.io.FileNotFoundException;
37 import java.io.FileOutputStream;
38 import java.util.Arrays;
39 import java.util.Date;
40 import java.util.HashMap;
41 import java.util.Iterator;
42 import java.util.List;
44 import java.util.Random;
45 import java.util.Vector;
47 import javax.servlet.http.Cookie;
48 import javax.servlet.http.HttpServletRequest;
49 import javax.servlet.http.HttpServletResponse;
51 import mir.entity.Entity;
52 import mir.log.LoggerWrapper;
53 import mir.session.HTTPAdapters;
54 import mir.session.Request;
55 import mir.util.DateToMapAdapter;
56 import mir.util.InternetFunctions;
57 import mir.util.StringRoutines;
58 import mircoders.entity.EntityComment;
59 import mircoders.entity.EntityContent;
60 import mircoders.localizer.MirAdminInterfaceLocalizer;
62 import org.apache.commons.collections.ExtendedProperties;
67 private int maxIdentifier;
68 private LoggerWrapper logger;
70 private boolean logEnabled;
71 private boolean openPostingDisabled;
72 private boolean openPostingPassword;
73 private boolean cookieOnBlock;
74 private String articleBlockAction;
75 private String commentBlockAction;
77 private String configFile = MirGlobal.config().getStringWithHome("Abuse.Config");
80 private static final String IP_FILTER_TYPE="ip";
81 private static final String REGEXP_FILTER_TYPE="regexp";
82 private static String cookieName=MirGlobal.config().getString("Abuse.CookieName");
83 private static int cookieMaxAge = 60*60*MirGlobal.config().getInt("Abuse.CookieMaxAge");
86 logger = new LoggerWrapper("Global.Abuse");
87 filters = new Vector();
93 articleBlockAction = "";
94 commentBlockAction = "";
95 openPostingPassword = false;
96 openPostingDisabled = false;
97 cookieOnBlock = false;
102 public boolean checkIpFilter(String anIpAddress) {
103 synchronized (filters) {
104 Iterator i = filters.iterator();
106 while (i.hasNext()) {
107 Filter filter = (Filter) i.next();
110 if ( (filter.getType().equals(IP_FILTER_TYPE)) &&
111 InternetFunctions.isIpAddressInNetwork(anIpAddress, filter.getExpression())) {
112 logger.debug("ip match on " + filter.getExpression());
116 catch (Throwable t) {
117 logger.warn("error while checking ip address " + anIpAddress + " over network " + filter.expression + ": " + t.getMessage());
125 private boolean checkRegExpFilter(Entity anEntity) {
126 synchronized (filters) {
127 Iterator i = filters.iterator();
129 while (i.hasNext()) {
130 Filter filter = (Filter) i.next();
132 if (filter.getType().equals(REGEXP_FILTER_TYPE)) {
134 RE regularExpression = new RE(filter.getExpression());
136 Iterator j = anEntity.getFields().iterator();
137 while (j.hasNext()) {
138 String field = anEntity.getValue( (String) j.next());
140 if (field != null && regularExpression.isMatch(field.toLowerCase())) {
141 logger.debug("regexp match on " + filter.getExpression());
146 catch (Throwable t) {
147 logger.warn("error while checking entity with regexp " + filter.getExpression() + ": " + t.getMessage());
156 private void setCookie(HttpServletResponse aResponse) {
157 Random random = new Random();
159 Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000)));
160 cookie.setMaxAge(cookieMaxAge);
164 aResponse.addCookie(cookie);
167 private boolean checkCookie(List aCookies) {
168 if (getCookieOnBlock()) {
169 Iterator i = aCookies.iterator();
171 while (i.hasNext()) {
172 Cookie cookie = (Cookie) i.next();
174 if (cookie.getName().equals(cookieName)) {
175 logger.debug("cookie match");
184 public boolean checkRequest(Request aRequest, HttpServletResponse aResponse, String anId, boolean anIsComment) {
185 String address = "0.0.0.0";
186 String browser = "unknown";
189 HttpServletRequest request = null;
191 if (aRequest instanceof HTTPAdapters.HTTPParsedRequestAdapter) {
192 request = ((HTTPAdapters.HTTPParsedRequestAdapter) aRequest).getRequest();
194 else if (aRequest instanceof HTTPAdapters.HTTPRequestAdapter) {
195 request = ((HTTPAdapters.HTTPRequestAdapter) aRequest).getRequest();
198 browser = (String) request.getHeader("User-Agent");
199 address = request.getRemoteAddr();
200 cookies = Arrays.asList(request.getCookies());
204 logComment(address, anId , new Date(), browser);
206 logArticle(address, anId , new Date(), browser);
208 return checkCookie(cookies) || checkIpFilter(address);
211 public void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) {
213 long time = System.currentTimeMillis();
215 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleCommentOperationForName(commentBlockAction);
217 if (checkRequest(aRequest, aResponse, aComment.getId(), true) || checkRegExpFilter(aComment)) {
218 logger.debug("performing operation " + operation.getName());
219 operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("comment", aComment));
220 setCookie(aResponse);
223 logger.info("checkComment: " + (System.currentTimeMillis()-time) + "ms");
225 catch (Throwable t) {
226 t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));
227 logger.error("Abuse.checkComment: " + t.toString());
231 public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {
233 long time = System.currentTimeMillis();
235 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleArticleOperationForName(commentBlockAction);
237 if (checkRequest(aRequest, aResponse, anArticle.getId(), false) || checkRegExpFilter(anArticle)) {
238 logger.debug("performing operation " + operation.getName());
239 operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("content", anArticle));
240 setCookie(aResponse);
243 logger.info("checkArticle: " + (System.currentTimeMillis()-time) + "ms");
245 catch (Throwable t) {
246 t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));
247 logger.error("Abuse.checkArticle: " + t.toString());
251 public boolean getLogEnabled() {
255 public void setLogEnabled(boolean anEnabled) {
256 logEnabled = anEnabled;
260 public int getLogSize() {
264 public void setLogSize(int aSize) {
269 public boolean getOpenPostingDisabled() {
270 return openPostingDisabled;
273 public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {
274 openPostingDisabled = anOpenPostingDisabled;
277 public boolean getOpenPostingPassword() {
278 return openPostingPassword;
281 public void setOpenPostingPassword(boolean anOpenPostingPassword) {
282 openPostingPassword = anOpenPostingPassword;
285 public boolean getCookieOnBlock() {
286 return cookieOnBlock;
289 public void setCookieOnBlock(boolean aCookieOnBlock) {
290 cookieOnBlock = aCookieOnBlock;
293 public String getArticleBlockAction() {
294 return articleBlockAction;
297 public void setArticleBlockAction(String anAction) {
298 articleBlockAction = anAction;
301 public String getCommentBlockAction() {
302 return commentBlockAction;
305 public void setCommentBlockAction(String anAction) {
306 commentBlockAction = anAction;
310 public List getLog() {
312 List result = new Vector();
314 Iterator i = log.iterator();
315 while (i.hasNext()) {
316 LogEntry logEntry = (LogEntry) i.next();
317 Map entry = new HashMap();
319 entry.put("ip", logEntry.getIpNumber());
320 entry.put("id", logEntry.getId());
321 entry.put("timestamp", new DateToMapAdapter(logEntry.getTimeStamp()));
322 if (logEntry.getIsArticle())
323 entry.put("type", "content");
325 entry.put("type", "comment");
326 entry.put("browser", logEntry.getBrowserString());
335 public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser) {
336 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false));
339 public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser) {
340 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true));
345 ExtendedProperties configuration = new ExtendedProperties();
348 configuration = new ExtendedProperties(configFile);
350 catch (FileNotFoundException e) {
353 getFilterConfig(filters, "abuse.filter", configuration);
355 setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));
356 setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));
357 setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));
358 setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));
359 setLogSize(configuration.getInt("abuse.logSize", 10));
360 setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));
361 setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));
363 catch (Throwable t) {
364 throw new RuntimeException(t.toString());
369 ExtendedProperties configuration = new ExtendedProperties();
371 setFilterConfig(filters, "abuse.filter", configuration);
373 configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled()?"1":"0");
374 configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword()?"1":"0");
375 configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock()?"1":"0");
376 configuration.addProperty("abuse.logEnabled", getLogEnabled()?"1":"0");
377 configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));
378 configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());
379 configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());
381 configuration.save(new FileOutputStream(new File(configFile)), "Anti abuse configuration");
383 catch (Throwable t) {
384 throw new RuntimeException(t.toString());
388 public List getFilterTypes() {
389 List result = new Vector();
391 Map entry = new HashMap();
392 entry.put("resource", "ip");
393 entry.put("id", IP_FILTER_TYPE);
396 entry = new HashMap();
397 entry.put("resource", "regexp");
398 entry.put("id", REGEXP_FILTER_TYPE);
404 public List getArticleActions() {
406 List result = new Vector();
408 Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();
409 while (i.hasNext()) {
410 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
411 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
413 Map action = new HashMap();
414 action.put("resource", operation.getName());
415 action.put("identifier", operation.getName());
422 catch (Throwable t) {
423 throw new RuntimeException("can't get article actions");
427 public List getCommentActions() {
429 List result = new Vector();
431 Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();
432 while (i.hasNext()) {
433 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
434 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
436 Map action = new HashMap();
437 action.put("resource", operation.getName());
438 action.put("identifier", operation.getName());
445 catch (Throwable t) {
446 throw new RuntimeException("can't get comment actions");
450 public List getFilters() {
451 return getFiltersAsMaps(filters);
454 public void addFilter(String aType, String anExpression) {
455 addFilter(filters, aType, anExpression);
458 public void setFilter(String anIdentifier, String aType, String anExpression) {
459 setFilter(filters, anIdentifier, aType, anExpression);
462 public void deleteFilter(String anIdentifier) {
463 deleteFilter(filters, anIdentifier);
466 public void validateIpFilter(String anIdentifier, String anArticleAction, String aCommentAction) throws Exception {
469 private List getFiltersAsMaps(List aFilters) {
470 synchronized(aFilters) {
471 List result = new Vector();
473 Iterator i = aFilters.iterator();
474 while (i.hasNext()) {
475 Filter filter = (Filter) i.next();
476 Map map = new HashMap();
478 map.put("id", filter.getId());
479 map.put("expression", filter.getExpression());
480 map.put("type", filter.getType());
488 private void addFilter(List aFilters, String aType, String anExpression) {
489 Filter filter = new Filter();
491 filter.setId(generateId());
492 filter.setExpression(anExpression);
493 filter.setType(aType);
495 synchronized (aFilters) {
496 aFilters.add(filter);
500 private void setFilter(List aFilters, String anIdentifier, String aType, String anExpression) {
501 synchronized (aFilters) {
502 Filter filter = findFilter(aFilters, anIdentifier);
505 filter.setExpression(anExpression);
506 filter.setType(aType);
511 private Filter findFilter(List aFilters, String anIdentifier) {
512 synchronized (aFilters) {
513 Iterator i = aFilters.iterator();
514 while (i.hasNext()) {
515 Filter filter = (Filter) i.next();
517 if (filter.getId().equals(anIdentifier)) {
526 private void deleteFilter(List aFilters, String anIdentifier) {
527 synchronized (aFilters) {
528 Filter filter = findFilter(aFilters, anIdentifier);
531 aFilters.remove(filter);
536 private String generateId() {
538 maxIdentifier = maxIdentifier+1;
540 return Integer.toString(maxIdentifier);
544 private static class Filter {
545 private String identifier;
546 private String expression;
555 public String getId() {
559 public void setId(String anId) {
563 public String getExpression() {
567 public void setExpression(String anExpression) {
568 expression = anExpression;
571 public String getType() {
575 public void setType(String aType) {
580 private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {
581 synchronized(aFilters) {
582 Iterator i = aFilters.iterator();
584 while (i.hasNext()) {
585 Filter filter = (Filter) i.next();
587 aConfiguration.addProperty(aConfigKey, filter.getType()+":"+filter.getExpression());
592 private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {
593 synchronized(aFilters) {
596 Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).iterator();
598 while (i.hasNext()) {
599 String filter = (String) i.next();
600 List parts = StringRoutines.separateString(filter, ":");
602 if (parts.size()==2) {
603 addFilter( (String) parts.get(0), (String) parts.get(1));
609 private static class LogEntry {
610 private String ipNumber;
611 private String browserString;
613 private Date timeStamp;
614 private boolean isArticle;
616 public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {
617 ipNumber = anIpNumber;
618 browserString = aBrowserString;
620 isArticle = anIsArticle;
621 timeStamp=aTimeStamp;
624 public String getIpNumber() {
628 public String getBrowserString() {
629 return browserString;
632 public String getId() {
636 public Date getTimeStamp() {
640 public boolean getIsArticle() {
645 private void truncateLog() {
650 while (log.size()>0 && log.size()>logSize) {
657 private void appendLog(LogEntry anEntry) {