f21885c7adee80cbe584dbb9ec4ac26036b9ad47
[mir.git] / source / mircoders / abuse / FilterEngine.java
1 /*
2  * Copyright (C) 2001, 2002 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
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.
10  *
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.
15  *
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
19  *
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.
29  */
30
31 package mircoders.abuse;
32
33 import mir.entity.Entity;
34 import mir.entity.adapter.EntityAdapter;
35 import mir.entity.adapter.EntityAdapterModel;
36 import mir.entity.adapter.EntityIteratorAdapter;
37 import mir.log.LoggerWrapper;
38 import mir.session.Request;
39 import mir.storage.DatabaseExc;
40 import mir.storage.DatabaseHelper;
41 import mircoders.global.MirGlobal;
42 import mircoders.storage.DatabaseFilter;
43 import mircoders.storage.DatabaseFilterGroup;
44
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.Date;
48 import java.util.HashMap;
49 import java.util.Iterator;
50 import java.util.List;
51 import java.util.Map;
52 /** The FilterEngine manages a list of all filters and filter groups.
53  *  Use the testPosting() method to apply all filters on an 
54  *  Entity (for ex. an article or a comment)
55  */
56 public class FilterEngine {
57   private final Map filterTypes = new HashMap();
58   private final List filterTypeIds = new ArrayList();
59
60   private final List filterGroups = new ArrayList();
61   private final Map idToFilterGroup = new HashMap();
62   private final LoggerWrapper logger = new LoggerWrapper("Global.Abuse.FilterEngine");
63   private EntityAdapterModel model;
64
65   public FilterEngine(EntityAdapterModel aModel) {
66     try {
67       Iterator i = MirGlobal.localizer().openPostings().getAntiAbuseFilterTypes().iterator();
68       while (i.hasNext()) {
69         FilterType filterType = (FilterType) i.next();
70
71         filterTypes.put(filterType.getName(), filterType);
72         filterTypeIds.add(filterType.getName());
73       }
74     }
75     catch (Throwable t) {
76       throw new RuntimeException(t.getMessage());
77     }
78
79     model = aModel;
80
81     reload();
82   }
83
84   /** applies all filters from all filter groups to an Entity.
85    *  The entity may be, for example, an article, or a comment.
86    *  It returns a filter that matches if it finds one, null otherwise  
87    */
88   public synchronized Filter testPosting(Entity anEntity, Request aRequest) {
89     Iterator i = filterGroups.iterator();
90     while (i.hasNext()) {
91       FilterGroup group = (FilterGroup) i.next();
92
93       Iterator j = group.getFilters().iterator();
94       while (j.hasNext()) {
95         Filter filter = (Filter) j.next();
96         try {
97           if (filter.test(anEntity, aRequest)) {
98             return filter;
99           }
100         }
101         catch (Throwable t) {
102           logger.warn("Exception thrown while testing filter " + filter.getType() + " ( " + filter.getExpression() + ") " + t.toString());
103         }
104       }
105     }
106
107     return null;
108   }
109
110   public List getFilterTypes() {
111     try {
112       List result = new ArrayList();
113
114       Iterator i = filterTypeIds.iterator();
115       while (i.hasNext()) {
116         String id = (String) i.next();
117
118         Map action = new HashMap();
119         action.put("resource", id);
120         action.put("identifier", id);
121
122         result.add(action);
123       }
124
125       return result;
126     }
127     catch (Throwable t) {
128       throw new RuntimeException("can't get article actions: " + t.getMessage());
129     }
130   }
131   /** This class reflects a row of the filter_group 
132    * database table. Filters groups allow you to organize 
133    * your filters. For example: group1=spammer ,  
134    * group2=nazis etc.  
135    */
136   public class FilterGroup {
137     private List filters;
138     private Entity entity;
139     private Map idToFilter;
140
141     public FilterGroup(Entity anEntity) {
142       this (anEntity, Collections.EMPTY_LIST);
143     }
144
145     public FilterGroup(Entity anEntity, List aFilters) {
146       entity = anEntity;
147       filters = new ArrayList();
148       idToFilter = new HashMap();
149       Iterator i = aFilters.iterator();
150
151       while (i.hasNext()) {
152         Entity entity = (Entity) i.next();
153         try {
154           Filter filter = new Filter(entity);
155           introduceFilter(filter);
156         }
157         catch (Throwable e) {
158           logger.debug("Misbehaving filer: " + entity.toString() + ": " + e);
159         }
160       }
161     }
162
163     public Entity getEntity() {
164       return entity;
165     }
166
167     public EntityAdapter getEntityAdapter() {
168       return model.makeEntityAdapter("filterGroup",  entity);
169     }
170
171     public List getFilterEntityAdapterList() {
172       List result = new ArrayList();
173
174       Iterator i = filters.iterator();
175       while (i.hasNext()) {
176         Filter filter = (Filter) i.next();
177         result.add(filter.getEntityAdapter());
178       }
179
180       return result;
181     }
182
183     public List getFilters() {
184       return filters;
185     }
186
187     public Filter getFilterForId(String anId) {
188       Filter result = (Filter) idToFilter.get(anId);
189       if (result==null) {
190         throw new NullPointerException("No such filter");
191       }
192
193       return result;
194     }
195
196     private void introduceFilter(Filter aFilter) {
197       filters.add(aFilter);
198       idToFilter.put(aFilter.getEntity().getId(), aFilter);
199     }
200
201     private void removeFilter(Filter aFilter) {
202       filters.remove(aFilter);
203       idToFilter.remove(aFilter.getEntity().getId());
204     }
205
206     private void deleteFilter(String anId) {
207       Filter filter = getFilterForId(anId);
208       removeFilter(filter);
209       DatabaseFilter.getInstance().delete(anId);
210     }
211
212     public void populateFilterEntity(Entity anEntity, String aType, String anExpression,
213                              String aComments, String aTag, String anArticleAction,
214                              String aCommentAction) {
215
216       anEntity.setFieldValue("type", aType);
217       anEntity.setFieldValue("expression", anExpression);
218       anEntity.setFieldValue("comment", aComments);
219       anEntity.setFieldValue("tag", aTag);
220       anEntity.setFieldValue("articleaction", anArticleAction);
221       anEntity.setFieldValue("commentaction", aCommentAction);
222     }
223
224     public String updateFilter(String anId, String aType, String anExpression,
225                              String aComments, String aTag, String anArticleAction,
226                              String aCommentAction) {
227
228       try {
229         getFilterTypeForId(aType).constructFilterInstance(anExpression);
230       }
231       catch (AbuseExc e) {
232         return e.getMessage();
233       }
234
235       Entity entity = getFilterForId(anId).getEntity();
236       populateFilterEntity(entity, aType, anExpression, aComments, aTag,
237           anArticleAction, aCommentAction);
238       entity.update();
239
240       return "";
241     }
242
243     public String createFilter(String aType, String anExpression,
244                              String aComments, String aTag, String anArticleAction,
245                              String aCommentAction) throws DatabaseExc {
246       FilterInstance instance;
247
248       try {
249         instance = getFilterTypeForId(aType).constructFilterInstance(anExpression);
250       }
251       catch (AbuseExc e) {
252         return e.getMessage();
253       }
254
255       Entity entity = DatabaseFilter.getInstance().createNewEntity();
256       populateFilterEntity(entity, aType, anExpression, aComments, aTag,
257           anArticleAction, aCommentAction);
258
259
260       String priority = "1";
261
262       if (filters.size() > 0) {
263         try {
264           String lastPriorityString = ((Filter) filters.get(filters.size()-1)).getEntity().getFieldValue("priority");
265           int lastPriority = Integer.parseInt(lastPriorityString);
266           priority = Integer.toString(lastPriority + 1);
267         }
268         catch (Throwable e) {
269         }
270       }
271
272       entity.setFieldValue("priority", priority);
273       entity.setFieldValue("filter_group_id", getEntity().getId());
274       entity.insert();
275
276       Filter filter = new Filter(entity, instance);
277       introduceFilter(filter);
278
279       return "";
280     }
281
282     public String moveFilterUp(String anId) {
283       Filter filter = getFilterForId(anId);
284       String priority = filter.getEntity().getFieldValue("priority");
285       int index = filters.indexOf(filter);
286       if (index>=1) {
287         Filter filterBefore= (Filter) filters.remove(index-1);
288         filters.add(index, filterBefore);
289         filter.getEntity().setFieldValue("priority", filterBefore.getEntity().getFieldValue("priority"));
290         filterBefore.getEntity().setFieldValue("priority", priority);
291         filter.getEntity().update();
292         filterBefore.getEntity().update();
293       }
294
295       return "";
296     }
297
298     public String moveFilterToTop(String anId) {
299       Filter filter = getFilterForId(anId);
300       String priority = filter.getEntity().getFieldValue("priority");
301       int index = filters.indexOf(filter);
302       if (index>0) {
303         filters.remove(index);
304         Filter filterBefore= (Filter) filters.get(0);
305         filters.add(0, filter);
306         filter.getEntity().setFieldValue("priority", filterBefore.getEntity().getFieldValue("priority"));
307         filterBefore.getEntity().setFieldValue("priority", priority);
308         filter.getEntity().update();
309         filterBefore.getEntity().update();
310       }
311
312       return "";
313     }
314
315     public String moveFilterDown(String anId) {
316       Filter filter = getFilterForId(anId);
317       String priority = filter.getEntity().getFieldValue("priority");
318       int index = filters.indexOf(filter);
319       if (index<filters.size()-1) {
320         Filter filterAfter = (Filter) filters.remove(index+1);
321         filters.add(index, filterAfter);
322         filter.getEntity().setFieldValue("priority", filterAfter.getEntity().getFieldValue("priority"));
323         filterAfter.getEntity().setFieldValue("priority", priority);
324         filter.getEntity().update();
325         filterAfter.getEntity().update();
326       }
327
328       return "";
329     }
330
331     public String moveFilterToBottom(String anId) {
332       Filter filter = getFilterForId(anId);
333       String priority = filter.getEntity().getFieldValue("priority");
334       int index = filters.indexOf(filter);
335       if (index>=0 && index<filters.size()-1) {
336         filters.remove(index);
337         Filter filterBefore= (Filter) filters.get(filters.size()-1);
338         filters.add(filters.size(), filter);
339         filter.getEntity().setFieldValue("priority", filterBefore.getEntity().getFieldValue("priority"));
340         filterBefore.getEntity().setFieldValue("priority", priority);
341         filter.getEntity().update();
342         filterBefore.getEntity().update();
343       }
344
345       return "";
346     }
347
348
349     public String getName() {
350       return entity.getFieldValue("name");
351     }
352   }
353   
354   /** This class reflects a row of the filter database table. 
355    * To actually run a filter on data, use the test() method. 
356    * This class will automatically retreive and use the correct 
357    * filter type.
358    */
359   public class Filter {
360     private Entity entity;
361     private FilterInstance instance;
362
363     public Filter(Entity anEntity) throws AbuseExc {
364       this(anEntity, getFilterTypeForId(anEntity.getFieldValue("type")).constructFilterInstance(anEntity.getFieldValue("expression")));
365     }
366
367     public Filter(Entity anEntity, FilterInstance anInstance) {
368       entity = anEntity;
369       instance = anInstance;
370     }
371
372     public Entity getEntity() {
373       return entity;
374     }
375
376     public EntityAdapter getEntityAdapter() {
377       return model.makeEntityAdapter("filter", entity);
378     }
379
380     public void update(String aType, String anExpression, String aComments, String aTag,
381                        String anArticleAction, String aCommentAction) throws AbuseExc {
382
383       instance = getFilterTypeForId(aType).constructFilterInstance(anExpression);
384
385       entity.setFieldValue("type", aType);
386       entity.setFieldValue("expression", anExpression);
387       entity.setFieldValue("tag", aType);
388       entity.setFieldValue("comment", aComments);
389       entity.setFieldValue("articleaction", anArticleAction);
390       entity.setFieldValue("commentaction", aCommentAction);
391       entity.setFieldValue("last_hit", null);
392       entity.update();
393    }
394
395     public void updateLastHit(Date aDate) {
396       entity.setFieldValue("last_hit",
397           DatabaseHelper.convertDateToInternalRepresenation(
398               new Date(System.currentTimeMillis())));
399       entity.update();
400     }
401
402     public String getType() {
403       return entity.getFieldValue("type");
404     }
405
406     public String getExpression() {
407       return entity.getFieldValue("expression");
408     }
409
410     public String getTag() {
411       return entity.getFieldValue("tag");
412     }
413
414     public String getComment() {
415       return entity.getFieldValue("comment");
416     }
417
418     public String getArticleAction() {
419       return entity.getFieldValue("articleaction");
420     }
421
422     public String getCommentAction() {
423       return entity.getFieldValue("commentaction");
424     }
425
426     public FilterInstance getInstance() {
427       return instance;
428     }
429
430     public boolean test(Entity anEntity, Request aRequest) {
431       return instance.test(anEntity, aRequest);
432     }
433   }
434
435   public synchronized void reload() {
436     filterGroups.clear();
437     idToFilterGroup.clear();
438
439     try {
440       Iterator i = new EntityIteratorAdapter("", "priority asc", 100, model, "filterGroup");
441       while (i.hasNext()) {
442         EntityAdapter entityAdapter = (EntityAdapter) i.next();
443         List filters = new ArrayList();
444         Iterator j = (Iterator) entityAdapter.getIterator("to_filters");
445         while (j.hasNext()) {
446           filters.add(((EntityAdapter) j.next()).getEntity());
447         }
448
449         FilterGroup filterGroup = new FilterGroup(entityAdapter.getEntity(), filters);
450         introduceFilterGroup(filterGroup);
451       }
452     }
453     catch (Throwable e) {
454       logger.error("Can't load filters: " + e.getMessage(), e);
455     }
456   }
457
458   public synchronized List getFilterGroups() {
459     List result = new ArrayList();
460     Iterator i = filterGroups.iterator();
461     while (i.hasNext()) {
462       result.add(((FilterGroup) i.next()).getEntityAdapter());
463     }
464
465     return result;
466   }
467
468   public synchronized void updateFilterGroup(String anId, String aName) {
469     FilterGroup filterGroup = getFilterGroupForId(anId);
470     filterGroup.getEntity().setFieldValue("name", aName);
471     filterGroup.getEntity().update();
472   }
473
474   public synchronized void addFilterGroup(String aName) throws DatabaseExc {
475     Entity entity = DatabaseFilterGroup.getInstance().createNewEntity();
476     entity.setFieldValue("name", aName);
477
478     String priority = "1";
479
480     if (filterGroups.size() > 0) {
481       try {
482         String lastPriorityString = ((FilterGroup) filterGroups.get(filterGroups.size()-1)).getEntity().getFieldValue("priority");
483         int lastPriority = Integer.parseInt(lastPriorityString);
484         priority = Integer.toString(lastPriority + 1);
485       }
486       catch (Exception e) {
487       }
488     }
489     entity.setFieldValue("priority", priority);
490     entity.insert();
491
492     FilterGroup filterGroup = new FilterGroup(entity);
493     introduceFilterGroup(filterGroup);
494   }
495
496   public synchronized void moveFilterGroupUp(String anId) {
497     FilterGroup group = (FilterGroup) idToFilterGroup.get(anId);
498     String priority = group.getEntity().getFieldValue("priority");
499     int index = filterGroups.indexOf(group);
500     if (index>=1) {
501       FilterGroup groupBefore = (FilterGroup) filterGroups.remove(index-1);
502       filterGroups.add(index, groupBefore);
503       group.getEntity().setFieldValue("priority", groupBefore.getEntity().getFieldValue("priority"));
504       groupBefore.getEntity().setFieldValue("priority", priority);
505       group.getEntity().update();
506       groupBefore.getEntity().update();
507     }
508   }
509
510   public synchronized void moveFilterGroupDown(String anId) {
511     FilterGroup group = (FilterGroup) idToFilterGroup.get(anId);
512     String priority = group.getEntity().getFieldValue("priority");
513     int index = filterGroups.indexOf(group);
514     if (index<filterGroups.size()-1) {
515       FilterGroup groupAfter = (FilterGroup) filterGroups.remove(index+1);
516       filterGroups.add(index, groupAfter);
517       group.getEntity().setFieldValue("priority", groupAfter.getEntity().getFieldValue("priority"));
518       groupAfter.getEntity().setFieldValue("priority", priority);
519       group.getEntity().update();
520       groupAfter.getEntity().update();
521     }
522   }
523
524   public synchronized void deleteFilterGroup(String anId) {
525
526     FilterGroup filterGroup = getFilterGroupForId(anId);
527     removeFilterGroup(filterGroup);
528     DatabaseFilter.getInstance().deleteByWhereClause("filter_group_id = " + anId);
529     DatabaseFilterGroup.getInstance().delete(anId);
530   }
531
532   public synchronized void deleteFilter(String aGroupId, String anId) {
533     getFilterGroupForId(aGroupId).deleteFilter(anId);
534   }
535
536
537   public synchronized String updateFilter(String aGroupId, String anId,
538                                         String aType, String anExpression,
539                                         String aComments,
540                                         String aTag,
541                                         String anArticleAction,
542                                         String aCommentAction) {
543     return getFilterGroupForId(aGroupId).updateFilter(anId, aType,
544         anExpression, aComments, aTag, anArticleAction, aCommentAction);
545   }
546
547   public synchronized String addFilter(String aGroupId,
548                                        String aType, String anExpression,
549                                        String aComments,
550                                        String aTag,
551                                        String anArticleAction,
552                                        String aCommentAction) throws DatabaseExc {
553     return getFilterGroupForId(aGroupId).createFilter(aType, anExpression,
554         aComments, aTag, anArticleAction, aCommentAction);
555   }
556
557   public synchronized String moveFilterUp(String aGroupId, String anId) {
558     return getFilterGroupForId(aGroupId).moveFilterUp(anId);
559   }
560
561   public synchronized String moveFilterDown(String aGroupId, String anId) {
562     return getFilterGroupForId(aGroupId).moveFilterDown(anId);
563   }
564
565   public synchronized String moveFilterToTop(String aGroupId, String anId) {
566     return getFilterGroupForId(aGroupId).moveFilterToTop(anId);
567   }
568
569   public synchronized String moveFilterToBottom(String aGroupId, String anId) {
570     return getFilterGroupForId(aGroupId).moveFilterToBottom(anId);
571   }
572
573
574
575   public FilterGroup getFilterGroupForId(String anId) {
576     FilterGroup result = (FilterGroup) idToFilterGroup.get(anId);
577     if (result == null) {
578       throw new NullPointerException("No such filter group");
579     }
580
581     return result;
582   }
583
584   public Filter getFilterForId(String aGroupId, String anId) {
585     return getFilterGroupForId(aGroupId).getFilterForId(anId);
586   }
587
588
589   public List getFilters(String aFilterGroupId) {
590     return getFilterGroupForId(aFilterGroupId).getFilterEntityAdapterList();
591   }
592
593   private synchronized void introduceFilterGroup(FilterGroup aFilterGroup) {
594     filterGroups.add(aFilterGroup);
595     idToFilterGroup.put(aFilterGroup.getEntity().getId(), aFilterGroup);
596   }
597
598   private synchronized void removeFilterGroup(FilterGroup aFilterGroup) {
599     filterGroups.remove(aFilterGroup);
600     idToFilterGroup.remove(aFilterGroup.getEntity().getId());
601   }
602
603   private FilterType getFilterTypeForId(String anId) {
604     return (FilterType) filterTypes.get(anId);
605   }
606 }