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;
34 import java.io.FileNotFoundException;
35 import java.io.FileOutputStream;
36 import java.util.Arrays;
37 import java.util.Date;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.List;
42 import java.util.Random;
43 import java.util.Vector;
44 import javax.servlet.http.Cookie;
45 import javax.servlet.http.HttpServletRequest;
46 import javax.servlet.http.HttpServletResponse;
48 import org.apache.commons.collections.ExtendedProperties;
51 import mir.config.MirPropertiesConfiguration;
52 import mir.entity.Entity;
53 import mir.log.LoggerWrapper;
54 import mir.session.HTTPAdapters;
55 import mir.session.Request;
56 import mir.util.GeneratorFormatAdapters;
57 import mir.util.InternetFunctions;
58 import mir.util.StringRoutines;
60 import mircoders.entity.EntityComment;
61 import mircoders.entity.EntityContent;
62 import mircoders.entity.EntityUsers;
63 import mircoders.localizer.MirAdminInterfaceLocalizer;
68 private int maxIdentifier;
69 private LoggerWrapper logger;
70 private LoggerWrapper adminUsageLogger;
72 private boolean logEnabled;
73 private boolean openPostingDisabled;
74 private boolean openPostingPassword;
75 private boolean cookieOnBlock;
76 private String articleBlockAction;
77 private String commentBlockAction;
79 private String configFile = MirGlobal.config().getStringWithHome("Abuse.Config");
81 private MirPropertiesConfiguration configuration;
84 private static final String IP_FILTER_TYPE="ip";
85 private static final String REGEXP_FILTER_TYPE="regexp";
86 private static String cookieName=MirGlobal.config().getString("Abuse.CookieName");
87 private static int cookieMaxAge = 60*60*MirGlobal.config().getInt("Abuse.CookieMaxAge");
90 logger = new LoggerWrapper("Global.Abuse");
91 adminUsageLogger = new LoggerWrapper("AdminUsage");
92 filters = new Vector();
97 configuration = MirPropertiesConfiguration.instance();
100 throw new RuntimeException("Can't get configuration: " + e.getMessage());
105 articleBlockAction = "";
106 commentBlockAction = "";
107 openPostingPassword = false;
108 openPostingDisabled = false;
109 cookieOnBlock = false;
114 public boolean checkIpFilter(String anIpAddress) {
115 synchronized (filters) {
116 Iterator i = filters.iterator();
118 while (i.hasNext()) {
119 Filter filter = (Filter) i.next();
122 if ( (filter.getType().equals(IP_FILTER_TYPE)) &&
123 InternetFunctions.isIpAddressInNetwork(anIpAddress, filter.getExpression())) {
124 logger.debug("ip match on " + filter.getExpression());
128 catch (Throwable t) {
129 logger.warn("error while checking ip address " + anIpAddress + " over network " + filter.expression + ": " + t.getMessage());
137 private boolean checkRegExpFilter(Entity anEntity) {
138 synchronized (filters) {
139 Iterator i = filters.iterator();
141 while (i.hasNext()) {
142 Filter filter = (Filter) i.next();
144 if (filter.getType().equals(REGEXP_FILTER_TYPE)) {
146 RE regularExpression = new RE(filter.getExpression(), RE.REG_ICASE);
148 Iterator j = anEntity.getFields().iterator();
149 while (j.hasNext()) {
150 String field = anEntity.getValue( (String) j.next());
152 if (field != null && regularExpression.isMatch(field.toLowerCase())) {
153 logger.debug("regexp match on " + filter.getExpression());
158 catch (Throwable t) {
159 logger.warn("error while checking entity with regexp " + filter.getExpression() + ": " + t.getMessage());
168 private void setCookie(HttpServletResponse aResponse) {
169 Random random = new Random();
171 Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000)));
172 cookie.setMaxAge(cookieMaxAge);
176 aResponse.addCookie(cookie);
179 private boolean checkCookie(List aCookies) {
180 if (getCookieOnBlock()) {
181 Iterator i = aCookies.iterator();
183 while (i.hasNext()) {
184 Cookie cookie = (Cookie) i.next();
186 if (cookie.getName().equals(cookieName)) {
187 logger.debug("cookie match");
196 public boolean checkRequest(Request aRequest, HttpServletResponse aResponse, String anId, boolean anIsComment) {
197 String address = "0.0.0.0";
198 String browser = "unknown";
201 HttpServletRequest request = null;
203 if (aRequest instanceof HTTPAdapters.HTTPParsedRequestAdapter) {
204 request = ((HTTPAdapters.HTTPParsedRequestAdapter) aRequest).getRequest();
206 else if (aRequest instanceof HTTPAdapters.HTTPRequestAdapter) {
207 request = ((HTTPAdapters.HTTPRequestAdapter) aRequest).getRequest();
210 browser = (String) request.getHeader("User-Agent");
211 address = request.getRemoteAddr();
212 cookies = Arrays.asList(request.getCookies());
216 logComment(address, anId , new Date(), browser);
218 logArticle(address, anId , new Date(), browser);
220 return checkCookie(cookies) || checkIpFilter(address);
223 public void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) {
225 long time = System.currentTimeMillis();
227 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleCommentOperationForName(commentBlockAction);
229 if (checkRequest(aRequest, aResponse, aComment.getId(), true) || checkRegExpFilter(aComment)) {
230 logger.debug("performing operation " + operation.getName());
231 operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("comment", aComment));
232 setCookie(aResponse);
235 logger.info("checkComment: " + (System.currentTimeMillis()-time) + "ms");
237 catch (Throwable t) {
238 t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));
239 logger.error("Abuse.checkComment: " + t.toString());
243 public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {
245 long time = System.currentTimeMillis();
247 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleArticleOperationForName(articleBlockAction);
249 if (checkRequest(aRequest, aResponse, anArticle.getId(), false) || checkRegExpFilter(anArticle)) {
250 logger.debug("performing operation " + operation.getName());
251 operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("content", anArticle));
252 setCookie(aResponse);
255 logger.info("checkArticle: " + (System.currentTimeMillis()-time) + "ms");
257 catch (Throwable t) {
258 t.printStackTrace(logger.asPrintWriter(logger.DEBUG_MESSAGE));
259 logger.error("Abuse.checkArticle: " + t.toString());
263 public boolean getLogEnabled() {
267 public void setLogEnabled(boolean anEnabled) {
268 if (!configuration.getString("Abuse.DisallowIPLogging", "0").equals("1"))
269 logEnabled = anEnabled;
273 public int getLogSize() {
277 public void setLogSize(int aSize) {
282 public boolean getOpenPostingDisabled() {
283 return openPostingDisabled;
286 public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {
287 openPostingDisabled = anOpenPostingDisabled;
290 public boolean getOpenPostingPassword() {
291 return openPostingPassword;
294 public void setOpenPostingPassword(boolean anOpenPostingPassword) {
295 openPostingPassword = anOpenPostingPassword;
298 public boolean getCookieOnBlock() {
299 return cookieOnBlock;
302 public void setCookieOnBlock(boolean aCookieOnBlock) {
303 cookieOnBlock = aCookieOnBlock;
306 public String getArticleBlockAction() {
307 return articleBlockAction;
310 public void setArticleBlockAction(String anAction) {
311 articleBlockAction = anAction;
314 public String getCommentBlockAction() {
315 return commentBlockAction;
318 public void setCommentBlockAction(String anAction) {
319 commentBlockAction = anAction;
323 public List getLog() {
326 List result = new Vector();
328 Iterator i = log.iterator();
329 while (i.hasNext()) {
330 LogEntry logEntry = (LogEntry) i.next();
331 Map entry = new HashMap();
333 entry.put("ip", logEntry.getIpNumber());
334 entry.put("id", logEntry.getId());
335 entry.put("timestamp", new GeneratorFormatAdapters.DateFormatAdapter(logEntry.getTimeStamp(), MirPropertiesConfiguration.instance().getString("Mir.DefaultTimezone")));
336 if (logEntry.getIsArticle())
337 entry.put("type", "content");
339 entry.put("type", "comment");
340 entry.put("browser", logEntry.getBrowserString());
347 catch (Throwable t) {
348 throw new RuntimeException(t.toString());
353 public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser) {
354 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false));
357 public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser) {
358 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true));
363 ExtendedProperties configuration = new ExtendedProperties();
366 configuration = new ExtendedProperties(configFile);
368 catch (FileNotFoundException e) {
371 getFilterConfig(filters, "abuse.filter", configuration);
373 setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));
374 setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));
375 setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));
376 setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));
377 setLogSize(configuration.getInt("abuse.logSize", 10));
378 setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));
379 setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));
381 catch (Throwable t) {
382 throw new RuntimeException(t.toString());
387 ExtendedProperties configuration = new ExtendedProperties();
389 setFilterConfig(filters, "abuse.filter", configuration);
391 configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled()?"1":"0");
392 configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword()?"1":"0");
393 configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock()?"1":"0");
394 configuration.addProperty("abuse.logEnabled", getLogEnabled()?"1":"0");
395 configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));
396 configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());
397 configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());
399 configuration.save(new FileOutputStream(new File(configFile)), "Anti abuse configuration");
401 catch (Throwable t) {
402 throw new RuntimeException(t.toString());
406 public List getFilterTypes() {
407 List result = new Vector();
409 Map entry = new HashMap();
410 entry.put("resource", "ip");
411 entry.put("id", IP_FILTER_TYPE);
414 entry = new HashMap();
415 entry.put("resource", "regexp");
416 entry.put("id", REGEXP_FILTER_TYPE);
422 public List getArticleActions() {
424 List result = new Vector();
426 Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();
427 while (i.hasNext()) {
428 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
429 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
431 Map action = new HashMap();
432 action.put("resource", operation.getName());
433 action.put("identifier", operation.getName());
440 catch (Throwable t) {
441 throw new RuntimeException("can't get article actions");
445 public List getCommentActions() {
447 List result = new Vector();
449 Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();
450 while (i.hasNext()) {
451 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
452 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
454 Map action = new HashMap();
455 action.put("resource", operation.getName());
456 action.put("identifier", operation.getName());
463 catch (Throwable t) {
464 throw new RuntimeException("can't get comment actions");
468 public List getFilters() {
469 return getFiltersAsMaps(filters);
472 public void addFilter(String aType, String anExpression) {
473 addFilter(filters, aType, anExpression);
476 public void setFilter(String anIdentifier, String aType, String anExpression) {
477 setFilter(filters, anIdentifier, aType, anExpression);
480 public void deleteFilter(String anIdentifier) {
481 deleteFilter(filters, anIdentifier);
484 public void validateIpFilter(String anIdentifier, String anArticleAction, String aCommentAction) throws Exception {
487 private List getFiltersAsMaps(List aFilters) {
488 synchronized(aFilters) {
489 List result = new Vector();
491 Iterator i = aFilters.iterator();
492 while (i.hasNext()) {
493 Filter filter = (Filter) i.next();
494 Map map = new HashMap();
496 map.put("id", filter.getId());
497 map.put("expression", filter.getExpression());
498 map.put("type", filter.getType());
506 private void addFilter(List aFilters, String aType, String anExpression) {
507 Filter filter = new Filter();
509 filter.setId(generateId());
510 filter.setExpression(anExpression);
511 filter.setType(aType);
513 synchronized (aFilters) {
514 aFilters.add(filter);
518 private void setFilter(List aFilters, String anIdentifier, String aType, String anExpression) {
519 synchronized (aFilters) {
520 Filter filter = findFilter(aFilters, anIdentifier);
523 filter.setExpression(anExpression);
524 filter.setType(aType);
529 private Filter findFilter(List aFilters, String anIdentifier) {
530 synchronized (aFilters) {
531 Iterator i = aFilters.iterator();
532 while (i.hasNext()) {
533 Filter filter = (Filter) i.next();
535 if (filter.getId().equals(anIdentifier)) {
544 private void deleteFilter(List aFilters, String anIdentifier) {
545 synchronized (aFilters) {
546 Filter filter = findFilter(aFilters, anIdentifier);
549 aFilters.remove(filter);
554 private String generateId() {
556 maxIdentifier = maxIdentifier+1;
558 return Integer.toString(maxIdentifier);
562 private static class Filter {
563 private String identifier;
564 private String expression;
573 public String getId() {
577 public void setId(String anId) {
581 public String getExpression() {
585 public void setExpression(String anExpression) {
586 expression = anExpression;
589 public String getType() {
593 public void setType(String aType) {
598 private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {
599 synchronized(aFilters) {
600 Iterator i = aFilters.iterator();
602 while (i.hasNext()) {
603 Filter filter = (Filter) i.next();
605 aConfiguration.addProperty(aConfigKey, filter.getType()+":"+filter.getExpression());
610 private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {
611 synchronized(aFilters) {
614 Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).iterator();
616 while (i.hasNext()) {
617 String filter = (String) i.next();
618 List parts = StringRoutines.separateString(filter, ":");
620 if (parts.size()==2) {
621 addFilter( (String) parts.get(0), (String) parts.get(1));
627 private static class LogEntry {
628 private String ipNumber;
629 private String browserString;
631 private Date timeStamp;
632 private boolean isArticle;
634 public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {
635 ipNumber = anIpNumber;
636 browserString = aBrowserString;
638 isArticle = anIsArticle;
639 timeStamp=aTimeStamp;
642 public String getIpNumber() {
646 public String getBrowserString() {
647 return browserString;
650 public String getId() {
654 public Date getTimeStamp() {
658 public boolean getIsArticle() {
663 private void truncateLog() {
668 while (log.size()>0 && log.size()>logSize) {
675 private void appendLog(LogEntry anEntry) {
684 public void logAdminUsage(EntityUsers aUser, String aDescription) {
686 String user = "unknown (" + aUser.toString() +")";
688 user = aUser.getValue("login");
689 adminUsageLogger.info(user + ": " + aDescription);
691 catch (Throwable t) {
692 logger.error("Error while logging admin usage ("+aUser.toString()+", "+aDescription+"): " +t.toString());