1 package mircoders.global;
\r
4 import java.io.FileNotFoundException;
\r
5 import java.io.FileOutputStream;
\r
6 import java.util.Arrays;
\r
7 import java.util.Date;
\r
8 import java.util.HashMap;
\r
9 import java.util.Iterator;
\r
10 import java.util.List;
\r
11 import java.util.Map;
\r
12 import java.util.Random;
\r
13 import java.util.Vector;
\r
14 import javax.servlet.http.Cookie;
\r
15 import javax.servlet.http.HttpServletRequest;
\r
16 import javax.servlet.http.HttpServletResponse;
\r
18 import org.apache.commons.collections.ExtendedProperties;
\r
20 import gnu.regexp.RE;
\r
22 import mir.entity.Entity;
\r
23 import mir.log.LoggerWrapper;
\r
24 import mir.util.DateToMapAdapter;
\r
25 import mir.util.InternetFunctions;
\r
26 import mir.util.StringRoutines;
\r
27 import mircoders.entity.EntityComment;
\r
28 import mircoders.entity.EntityContent;
\r
29 import mircoders.localizer.MirAdminInterfaceLocalizer;
\r
32 public class Abuse {
\r
33 private List filters;
\r
34 private int maxIdentifier;
\r
35 private LoggerWrapper logger;
\r
36 private int logSize;
\r
37 private boolean logEnabled;
\r
38 private boolean openPostingDisabled;
\r
39 private boolean openPostingPassword;
\r
40 private boolean cookieOnBlock;
\r
41 private String articleBlockAction;
\r
42 private String commentBlockAction;
\r
44 private String configFile = MirGlobal.config().getStringWithHome("Abuse.Config");
\r
47 private static final String IP_FILTER_TYPE="ip";
\r
48 private static final String REGEXP_FILTER_TYPE="regexp";
\r
49 private static String cookieName=MirGlobal.config().getString("Abuse.CookieName");
\r
50 private static int cookieMaxAge = 60*60*MirGlobal.config().getInt("Abuse.CookieMaxAge");
\r
53 logger = new LoggerWrapper("Global.Abuse");
\r
54 filters = new Vector();
\r
60 articleBlockAction = "";
\r
61 commentBlockAction = "";
\r
62 openPostingPassword = false;
\r
63 openPostingDisabled = false;
\r
64 cookieOnBlock = false;
\r
69 public boolean checkIpFilter(String anIpAddress) {
\r
70 synchronized (filters) {
\r
71 Iterator i = filters.iterator();
\r
73 while (i.hasNext()) {
\r
74 Filter filter = (Filter) i.next();
\r
77 if ( (filter.getType().equals(IP_FILTER_TYPE)) &&
\r
78 InternetFunctions.isIpAddressInNetwork(anIpAddress, filter.getExpression())) {
\r
79 logger.debug("ip match on " + filter.getExpression());
\r
83 catch (Throwable t) {
\r
84 logger.warn("error while checking ip address " + anIpAddress + " over network " + filter.expression + ": " + t.getMessage());
\r
92 private boolean checkRegExpFilter(Entity anEntity) {
\r
93 synchronized (filters) {
\r
94 Iterator i = filters.iterator();
\r
96 while (i.hasNext()) {
\r
97 Filter filter = (Filter) i.next();
\r
99 if (filter.getType().equals(REGEXP_FILTER_TYPE)) {
\r
101 RE regularExpression = new RE(filter.getExpression());
\r
103 Iterator j = anEntity.getFields().iterator();
\r
104 while (j.hasNext()) {
\r
105 String field = anEntity.getValue( (String) j.next());
\r
107 if (field != null && regularExpression.isMatch(field.toLowerCase())) {
\r
108 logger.debug("regexp match on " + filter.getExpression());
\r
113 catch (Throwable t) {
\r
114 logger.warn("error while checking entity with regexp " + filter.getExpression() + ": " + t.getMessage());
\r
123 private void setCookie(HttpServletResponse aResponse) {
\r
124 Random random = new Random();
\r
126 Cookie cookie = new Cookie(cookieName, Integer.toString(random.nextInt(1000000000)));
\r
127 cookie.setMaxAge(cookieMaxAge);
\r
128 cookie.setPath("/");
\r
129 aResponse.addCookie(cookie);
\r
132 private boolean checkCookie(List aCookies) {
\r
133 if (getCookieOnBlock()) {
\r
134 Iterator i = aCookies.iterator();
\r
136 while (i.hasNext()) {
\r
137 Cookie cookie = (Cookie) i.next();
\r
139 if (cookie.getName().equals(cookieName)) {
\r
140 logger.debug("cookie match");
\r
149 public void checkComment(EntityComment aComment, HttpServletRequest aRequest, HttpServletResponse aResponse) {
\r
151 long time = System.currentTimeMillis();
\r
153 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleCommentOperationForName(commentBlockAction);
\r
155 if (checkCookie(Arrays.asList(aRequest.getCookies())) || checkIpFilter(aRequest.getRemoteAddr()) || checkRegExpFilter(aComment)) {
\r
156 operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("comment", aComment));
\r
157 setCookie(aResponse);
\r
160 logger.info("checkComment: " + (System.currentTimeMillis()-time) + "ms");
\r
163 catch (Throwable t) {
\r
164 logger.error("Abuse.checkComment: " + t.toString());
\r
168 public void checkArticle(EntityContent anArticle, HttpServletRequest aRequest, HttpServletResponse aResponse) {
\r
170 long time = System.currentTimeMillis();
\r
172 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation = MirGlobal.localizer().adminInterface().simpleCommentOperationForName(commentBlockAction);
\r
174 if (checkCookie(Arrays.asList(aRequest.getCookies())) || checkIpFilter(aRequest.getRemoteAddr()) || checkRegExpFilter(anArticle)) {
\r
175 operation.perform(null, MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("content", anArticle));
\r
176 setCookie(aResponse);
\r
179 logger.info("checkArticle: " + (System.currentTimeMillis()-time) + "ms");
\r
181 catch (Throwable t) {
\r
182 logger.error("Abuse.checkArticle: " + t.toString());
\r
186 public boolean getLogEnabled() {
\r
190 public void setLogEnabled(boolean anEnabled) {
\r
191 logEnabled = anEnabled;
\r
195 public int getLogSize() {
\r
199 public void setLogSize(int aSize) {
\r
204 public boolean getOpenPostingDisabled() {
\r
205 return openPostingDisabled;
\r
208 public void setOpenPostingDisabled(boolean anOpenPostingDisabled) {
\r
209 openPostingDisabled = anOpenPostingDisabled;
\r
212 public boolean getOpenPostingPassword() {
\r
213 return openPostingPassword;
\r
216 public void setOpenPostingPassword(boolean anOpenPostingPassword) {
\r
217 openPostingPassword = anOpenPostingPassword;
\r
220 public boolean getCookieOnBlock() {
\r
221 return cookieOnBlock;
\r
224 public void setCookieOnBlock(boolean aCookieOnBlock) {
\r
225 cookieOnBlock = aCookieOnBlock;
\r
228 public String getArticleBlockAction() {
\r
229 return articleBlockAction;
\r
232 public void setArticleBlockAction(String anAction) {
\r
233 articleBlockAction = anAction;
\r
236 public String getCommentBlockAction() {
\r
237 return commentBlockAction;
\r
240 public void setCommentBlockAction(String anAction) {
\r
241 commentBlockAction = anAction;
\r
245 public List getLog() {
\r
246 synchronized(log) {
\r
247 List result = new Vector();
\r
249 Iterator i = log.iterator();
\r
250 while (i.hasNext()) {
\r
251 LogEntry logEntry = (LogEntry) i.next();
\r
252 Map entry = new HashMap();
\r
254 entry.put("ip", logEntry.getIpNumber());
\r
255 entry.put("id", logEntry.getId());
\r
256 entry.put("timestamp", new DateToMapAdapter(logEntry.getTimeStamp()));
\r
257 if (logEntry.getIsArticle())
\r
258 entry.put("type", "content");
\r
260 entry.put("type", "comment");
\r
261 entry.put("browser", logEntry.getBrowserString());
\r
270 public void logComment(String anIp, String anId, Date aTimeStamp, String aBrowser) {
\r
271 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, false));
\r
274 public void logArticle(String anIp, String anId, Date aTimeStamp, String aBrowser) {
\r
275 appendLog(new LogEntry(aTimeStamp, anIp, aBrowser, anId, true));
\r
278 public void load() {
\r
280 ExtendedProperties configuration = new ExtendedProperties();
\r
283 configuration = new ExtendedProperties(configFile);
\r
285 catch (FileNotFoundException e) {
\r
288 getFilterConfig(filters, "abuse.filter", configuration);
\r
290 setOpenPostingDisabled(configuration.getString("abuse.openPostingDisabled", "0").equals("1"));
\r
291 setOpenPostingPassword(configuration.getString("abuse.openPostingPassword", "0").equals("1"));
\r
292 setCookieOnBlock(configuration.getString("abuse.cookieOnBlock", "0").equals("1"));
\r
293 setLogEnabled(configuration.getString("abuse.logEnabled", "0").equals("1"));
\r
294 setLogSize(configuration.getInt("abuse.logSize", 10));
\r
295 setArticleBlockAction(configuration.getString("abuse.articleBlockAction", ""));
\r
296 setCommentBlockAction(configuration.getString("abuse.commentBlockAction", ""));
\r
298 catch (Throwable t) {
\r
299 throw new RuntimeException(t.toString());
\r
302 public void save() {
\r
304 ExtendedProperties configuration = new ExtendedProperties();
\r
306 setFilterConfig(filters, "abuse.filter", configuration);
\r
308 configuration.addProperty("abuse.openPostingDisabled", getOpenPostingDisabled()?"1":"0");
\r
309 configuration.addProperty("abuse.openPostingPassword", getOpenPostingPassword()?"1":"0");
\r
310 configuration.addProperty("abuse.cookieOnBlock", getCookieOnBlock()?"1":"0");
\r
311 configuration.addProperty("abuse.logEnabled", getLogEnabled()?"1":"0");
\r
312 configuration.addProperty("abuse.logSize", Integer.toString(getLogSize()));
\r
313 configuration.addProperty("abuse.articleBlockAction", getArticleBlockAction());
\r
314 configuration.addProperty("abuse.commentBlockAction", getCommentBlockAction());
\r
316 configuration.save(new FileOutputStream(new File(configFile)), "Anti abuse configuration");
\r
318 catch (Throwable t) {
\r
319 throw new RuntimeException(t.toString());
\r
323 public List getFilterTypes() {
\r
324 List result = new Vector();
\r
326 Map entry = new HashMap();
\r
327 entry.put("resource", "abuse.filtertype.ip");
\r
328 entry.put("id", IP_FILTER_TYPE);
\r
331 entry = new HashMap();
\r
332 entry.put("resource", "abuse.filtertype.regexp");
\r
333 entry.put("id", REGEXP_FILTER_TYPE);
\r
339 public List getArticleActions() {
\r
341 List result = new Vector();
\r
343 Iterator i = MirGlobal.localizer().adminInterface().simpleArticleOperations().iterator();
\r
344 while (i.hasNext()) {
\r
345 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
\r
346 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
\r
348 Map action = new HashMap();
\r
349 action.put("resource", "content.operation."+operation.getName());
\r
350 action.put("identifier", operation.getName());
\r
352 result.add(action);
\r
357 catch (Throwable t) {
\r
358 throw new RuntimeException("can't get article actions");
\r
362 public List getCommentActions() {
\r
364 List result = new Vector();
\r
366 Iterator i = MirGlobal.localizer().adminInterface().simpleCommentOperations().iterator();
\r
367 while (i.hasNext()) {
\r
368 MirAdminInterfaceLocalizer.MirSimpleEntityOperation operation =
\r
369 (MirAdminInterfaceLocalizer.MirSimpleEntityOperation) i.next();
\r
371 Map action = new HashMap();
\r
372 action.put("resource", "comment.operation."+operation.getName());
\r
373 action.put("identifier", operation.getName());
\r
375 result.add(action);
\r
380 catch (Throwable t) {
\r
381 throw new RuntimeException("can't get comment actions");
\r
385 public List getFilters() {
\r
386 return getFiltersAsMaps(filters);
\r
389 public void addFilter(String aType, String anExpression) {
\r
390 addFilter(filters, aType, anExpression);
\r
393 public void setFilter(String anIdentifier, String aType, String anExpression) {
\r
394 setFilter(filters, anIdentifier, aType, anExpression);
\r
397 public void deleteFilter(String anIdentifier) {
\r
398 deleteFilter(filters, anIdentifier);
\r
401 public void validateIpFilter(String anIdentifier, String anArticleAction, String aCommentAction) throws Exception {
\r
404 private List getFiltersAsMaps(List aFilters) {
\r
405 synchronized(aFilters) {
\r
406 List result = new Vector();
\r
408 Iterator i = aFilters.iterator();
\r
409 while (i.hasNext()) {
\r
410 Filter filter = (Filter) i.next();
\r
411 Map map = new HashMap();
\r
413 map.put("id", filter.getId());
\r
414 map.put("expression", filter.getExpression());
\r
415 map.put("type", filter.getType());
\r
423 private void addFilter(List aFilters, String aType, String anExpression) {
\r
424 Filter filter = new Filter();
\r
426 filter.setId(generateId());
\r
427 filter.setExpression(anExpression);
\r
428 filter.setType(aType);
\r
430 synchronized (aFilters) {
\r
431 aFilters.add(filter);
\r
435 private void setFilter(List aFilters, String anIdentifier, String aType, String anExpression) {
\r
436 synchronized (aFilters) {
\r
437 Filter filter = findFilter(aFilters, anIdentifier);
\r
439 if (filter!=null) {
\r
440 filter.setExpression(anExpression);
\r
441 filter.setType(aType);
\r
446 private Filter findFilter(List aFilters, String anIdentifier) {
\r
447 synchronized (aFilters) {
\r
448 Iterator i = aFilters.iterator();
\r
449 while (i.hasNext()) {
\r
450 Filter filter = (Filter) i.next();
\r
452 if (filter.getId().equals(anIdentifier)) {
\r
461 private void deleteFilter(List aFilters, String anIdentifier) {
\r
462 synchronized (aFilters) {
\r
463 Filter filter = findFilter(aFilters, anIdentifier);
\r
465 if (filter!=null) {
\r
466 aFilters.remove(filter);
\r
471 private String generateId() {
\r
472 synchronized(this) {
\r
473 maxIdentifier = maxIdentifier+1;
\r
475 return Integer.toString(maxIdentifier);
\r
479 private static class Filter {
\r
480 private String identifier;
\r
481 private String expression;
\r
482 private String type;
\r
490 public String getId() {
\r
494 public void setId(String anId) {
\r
498 public String getExpression() {
\r
502 public void setExpression(String anExpression) {
\r
503 expression = anExpression;
\r
506 public String getType() {
\r
510 public void setType(String aType) {
\r
515 private void setFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {
\r
516 synchronized(aFilters) {
\r
517 Iterator i = aFilters.iterator();
\r
519 while (i.hasNext()) {
\r
520 Filter filter = (Filter) i.next();
\r
522 aConfiguration.addProperty(aConfigKey, filter.getType()+":"+filter.getExpression());
\r
527 private void getFilterConfig(List aFilters, String aConfigKey, ExtendedProperties aConfiguration) {
\r
528 synchronized(aFilters) {
\r
531 Iterator i = Arrays.asList(aConfiguration.getStringArray(aConfigKey)).iterator();
\r
533 while (i.hasNext()) {
\r
534 String filter = (String) i.next();
\r
535 List parts = StringRoutines.separateString(filter, ":");
\r
537 if (parts.size()==2) {
\r
538 addFilter( (String) parts.get(0), (String) parts.get(1));
\r
544 private static class LogEntry {
\r
545 private String ipNumber;
\r
546 private String browserString;
\r
548 private Date timeStamp;
\r
549 private boolean isArticle;
\r
551 public LogEntry(Date aTimeStamp, String anIpNumber, String aBrowserString, String anId, boolean anIsArticle) {
\r
552 ipNumber = anIpNumber;
\r
553 browserString = aBrowserString;
\r
555 isArticle = anIsArticle;
\r
556 timeStamp=aTimeStamp;
\r
559 public String getIpNumber() {
\r
563 public String getBrowserString() {
\r
564 return browserString;
\r
567 public String getId() {
\r
571 public Date getTimeStamp() {
\r
575 public boolean getIsArticle() {
\r
580 private void truncateLog() {
\r
581 synchronized(log) {
\r
585 while (log.size()>0 && log.size()>logSize) {
\r
592 private void appendLog(LogEntry anEntry) {
\r
593 synchronized (log) {
\r