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 void checkComment(EntityComment aComment, Request aRequest, HttpServletResponse aResponse) {
186 long time = System.currentTimeMillis();
187 String address = "0.0.0.0";
188 String browser = "unknown";
189 Cookie[] cookies = {};
191 HttpServletRequest request = null;
193 if (aRequest instanceof HTTPAdapters.HTTPParsedRequestAdapter) {
194 request = ((HTTPAdapters.HTTPParsedRequestAdapter) aRequest).getRequest();
196 else if (aRequest instanceof HTTPAdapters.HTTPRequestAdapter) {
197 request = ((HTTPAdapters.HTTPRequestAdapter) aRequest).getRequest();
200 browser = (String) request.getHeader("User-Agent");
201 address = request.getRemoteAddr();
202 cookies = request.getCookies();
205 logComment(address, aComment.getId(), new Date(), browser);
207 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleCommentOperationForName(commentBlockAction);
209 if (checkCookie(Arrays.asList(cookies)) || checkIpFilter(address) || checkRegExpFilter(aComment)) {
210 logger.debug("performing operation " + operation.getName());
211 operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("comment", aComment));
212 setCookie(aResponse);
215 logger.info("checkComment: " + (System.currentTimeMillis()-time) + "ms");
217 catch (Throwable t) {
218 logger.error("Abuse.checkComment: " + t.toString());
222 public void checkArticle(EntityContent anArticle, Request aRequest, HttpServletResponse aResponse) {
224 long time = System.currentTimeMillis();
225 String address = "0.0.0.0";
226 String browser = "unknown";
227 Cookie[] cookies = {};
229 HttpServletRequest request = null;
231 if (aRequest instanceof HTTPAdapters.HTTPParsedRequestAdapter) {
232 request = ((HTTPAdapters.HTTPParsedRequestAdapter) aRequest).getRequest();
234 else if (aRequest instanceof HTTPAdapters.HTTPRequestAdapter) {
235 request = ((HTTPAdapters.HTTPRequestAdapter) aRequest).getRequest();
238 browser = (String) request.getHeader("User-Agent");
239 address = request.getRemoteAddr();
240 cookies = request.getCookies();
243 logger.debug("no request available! " + aRequest.getClass().getName());
245 logArticle(address, anArticle.getId(), new Date(), browser);
247 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleArticleOperationForName(commentBlockAction);
249 if (checkCookie(Arrays.asList(cookies)) || checkIpFilter(address) || 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 logger.error("Abuse.checkArticle: " + t.toString());
262 public boolean getLogEnabled() {
266 public void setLogEnabled(boolean anEnabled) {
267 logEnabled = anEnabled;
271 public int getLogSize() {
275 public void setLogSize(int aSize) {
280 public boolean getOpenPostingDisabled() {
281 return openPostingDisabled;
284 public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {
285 openPostingDisabled = anOpenPostingDisabled;
288 public boolean getOpenPostingPassword() {
289 return openPostingPassword;
292 public void setOpenPostingPassword(boolean anOpenPostingPassword) {
293 openPostingPassword = anOpenPostingPassword;
296 public boolean getCookieOnBlock() {
297 return cookieOnBlock;
300 public void setCookieOnBlock(boolean aCookieOnBlock) {
301 cookieOnBlock = aCookieOnBlock;
304 public String getArticleBlockAction() {
305 return articleBlockAction;
308 public void setArticleBlockAction(String anAction) {
309 articleBlockAction = anAction;
312 public String getCommentBlockAction() {
313 return commentBlockAction;
316 public void setCommentBlockAction(String anAction) {
317 commentBlockAction = anAction;
321 public List getLog() {
323 List result = new Vector();
325 Iterator i = log.iterator();
326 while (i.hasNext()) {
327 LogEntry logEntry = (LogEntry) i.next();
328 Map entry = new HashMap();
330 entry.put("ip", logEntry.getIpNumber());
331 entry.put("id", logEntry.getId());
332 entry.put("timestamp", new DateToMapAdapter(logEntry.getTimeStamp()));
333 if (logEntry.getIsArticle())
334 entry.put("type", "content");
336 entry.put("type", "comment");
337 entry.put("browser", logEntry.getBrowserString());
346 public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser) {
347 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false));
350 public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser) {
351 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true));
356 ExtendedProperties configuration = new ExtendedProperties();
359 configuration = new ExtendedProperties(configFile);
361 catch (FileNotFoundException e) {
364 getFilterConfig(filters, "abuse.filter", configuration);
366 setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));
367 setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));
368 setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));
369 setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));
370 setLogSize(configuration.getInt("abuse.logSize", 10));
371 setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));
372 setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));
374 catch (Throwable t) {
375 throw new RuntimeException(t.toString());
380 ExtendedProperties configuration = new ExtendedProperties();
382 setFilterConfig(filters, "abuse.filter", configuration);
384 configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled()?"1":"0");
385 configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword()?"1":"0");
386 configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock()?"1":"0");
387 configuration.addProperty("abuse.logEnabled", getLogEnabled()?"1":"0");
388 configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));
389 configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());
390 configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());
392 configuration.save(new FileOutputStream(new File(configFile)), "Anti abuse configuration");
394 catch (Throwable t) {
395 throw new RuntimeException(t.toString());
399 public List getFilterTypes() {
400 List result = new Vector();
402 Map entry = new HashMap();
403 entry.put("resource", "ip");
404 entry.put("id", IP_FILTER_TYPE);
407 entry = new HashMap();
408 entry.put("resource", "regexp");
409 entry.put("id", REGEXP_FILTER_TYPE);
415 public List getArticleActions() {
417 List result = new Vector();
419 Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();
420 while (i.hasNext()) {
421 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
422 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
424 Map action = new HashMap();
425 action.put("resource", operation.getName());
426 action.put("identifier", operation.getName());
433 catch (Throwable t) {
434 throw new RuntimeException("can't get article actions");
438 public List getCommentActions() {
440 List result = new Vector();
442 Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();
443 while (i.hasNext()) {
444 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
445 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
447 Map action = new HashMap();
448 action.put("resource", operation.getName());
449 action.put("identifier", operation.getName());
456 catch (Throwable t) {
457 throw new RuntimeException("can't get comment actions");
461 public List getFilters() {
462 return getFiltersAsMaps(filters);
465 public void addFilter(String aType, String anExpression) {
466 addFilter(filters, aType, anExpression);
469 public void setFilter(String anIdentifier, String aType, String anExpression) {
470 setFilter(filters, anIdentifier, aType, anExpression);
473 public void deleteFilter(String anIdentifier) {
474 deleteFilter(filters, anIdentifier);
477 public void validateIpFilter(String anIdentifier, String anArticleAction, String aCommentAction) throws Exception {
480 private List getFiltersAsMaps(List aFilters) {
481 synchronized(aFilters) {
482 List result = new Vector();
484 Iterator i = aFilters.iterator();
485 while (i.hasNext()) {
486 Filter filter = (Filter) i.next();
487 Map map = new HashMap();
489 map.put("id", filter.getId());
490 map.put("expression", filter.getExpression());
491 map.put("type", filter.getType());
499 private void addFilter(List aFilters, String aType, String anExpression) {
500 Filter filter = new Filter();
502 filter.setId(generateId());
503 filter.setExpression(anExpression);
504 filter.setType(aType);
506 synchronized (aFilters) {
507 aFilters.add(filter);
511 private void setFilter(List aFilters, String anIdentifier, String aType, String anExpression) {
512 synchronized (aFilters) {
513 Filter filter = findFilter(aFilters, anIdentifier);
516 filter.setExpression(anExpression);
517 filter.setType(aType);
522 private Filter findFilter(List aFilters, String anIdentifier) {
523 synchronized (aFilters) {
524 Iterator i = aFilters.iterator();
525 while (i.hasNext()) {
526 Filter filter = (Filter) i.next();
528 if (filter.getId().equals(anIdentifier)) {
537 private void deleteFilter(List aFilters, String anIdentifier) {
538 synchronized (aFilters) {
539 Filter filter = findFilter(aFilters, anIdentifier);
542 aFilters.remove(filter);
547 private String generateId() {
549 maxIdentifier = maxIdentifier+1;
551 return Integer.toString(maxIdentifier);
555 private static class Filter {
556 private String identifier;
557 private String expression;
566 public String getId() {
570 public void setId(String anId) {
574 public String getExpression() {
578 public void setExpression(String anExpression) {
579 expression = anExpression;
582 public String getType() {
586 public void setType(String aType) {
591 private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {
592 synchronized(aFilters) {
593 Iterator i = aFilters.iterator();
595 while (i.hasNext()) {
596 Filter filter = (Filter) i.next();
598 aConfiguration.addProperty(aConfigKey, filter.getType()+":"+filter.getExpression());
603 private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {
604 synchronized(aFilters) {
607 Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).iterator();
609 while (i.hasNext()) {
610 String filter = (String) i.next();
611 List parts = StringRoutines.separateString(filter, ":");
613 if (parts.size()==2) {
614 addFilter( (String) parts.get(0), (String) parts.get(1));
620 private static class LogEntry {
621 private String ipNumber;
622 private String browserString;
624 private Date timeStamp;
625 private boolean isArticle;
627 public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {
628 ipNumber = anIpNumber;
629 browserString = aBrowserString;
631 isArticle = anIsArticle;
632 timeStamp=aTimeStamp;
635 public String getIpNumber() {
639 public String getBrowserString() {
640 return browserString;
643 public String getId() {
647 public Date getTimeStamp() {
651 public boolean getIsArticle() {
656 private void truncateLog() {
661 while (log.size()>0 && log.size()>logSize) {
668 private void appendLog(LogEntry anEntry) {