4032102ece72b409e1067afcae2c2f154ec9f721
[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 (AbuseExc e) {
158         }
159       }
160     }
161
162     public Entity getEntity() {
163       return entity;
164     }
165
166     public EntityAdapter getEntityAdapter() {
167       return model.makeEntityAdapter("filterGroup",  entity);
168     }
169
170     public List getFilterEntityAdapterList() {
171       List result = new ArrayList();
172
173       Iterator i = filters.iterator();
174       while (i.hasNext()) {
175         Filter filter = (Filter) i.next();
176         result.add(filter.getEntityAdapter());
177       }
178
179       return result;
180     }
181
182     public List getFilters() {
183       return filters;
184     }
185
186     public Filter getFilterForId(String anId) {
187       Filter result = (Filter) idToFilter.get(anId);
188       if (result==null) {
189         throw new NullPointerException("No such filter");
190       }
191
192       return result;
193     }
194
195     private void introduceFilter(Filter aFilter) {
196       filters.add(aFilter);
197       idToFilter.put(aFilter.getEntity().getId(), aFilter);
198     }
199
200     private void removeFilter(Filter aFilter) {
201       filters.remove(aFilter);
202       idToFilter.remove(aFilter.getEntity().getId());
203     }
204
205     private void deleteFilter(String anId) {
206       Filter filter = getFilterForId(anId);
207       removeFilter(filter);
208       DatabaseFilter.getInstance().delete(anId);
209     }
210
211     public void populateFilterEntity(Entity anEntity, String aType, String anExpression,
212                              String aComments, String aTag, String anArticleAction,
213                              String aCommentAction) {
214
215       anEntity.setFieldValue("type", aType);
216       anEntity.setFieldValue("expression", anExpression);
217       anEntity.setFieldValue("comment", aComments);
218       anEntity.setFieldValue("tag", aTag);
219       anEntity.setFieldValue("articleaction", anArticleAction);
220       anEntity.setFieldValue("commentaction", aCommentAction);
221     }
222
223     public String updateFilter(String anId, String aType, String anExpression,
224                              String aComments, String aTag, String anArticleAction,
225                              String aCommentAction) {
226
227       try {
228         getFilterTypeForId(aType).constructFilterInstance(anExpression);
229       }
230       catch (AbuseExc e) {
231         return e.getMessage();
232       }
233
234       Entity entity = getFilterForId(anId).getEntity();
235       populateFilterEntity(entity, aType, anExpression, aComments, aTag,
236           anArticleAction, aCommentAction);
237       entity.update();
238
239       return "";
240     }
241
242     public String createFilter(String aType, String anExpression,
243                              String aComments, String aTag, String anArticleAction,
244                              String aCommentAction) throws DatabaseExc {
245       FilterInstance instance;
246
247       try {
248         instance = getFilterTypeForId(aType).constructFilterInstance(anExpression);
249       }
250       catch (AbuseExc e) {
251         return e.getMessage();
252       }
253
254       Entity entity = DatabaseFilter.getInstance().createNewEntity();
255       populateFilterEntity(entity, aType, anExpression, aComments, aTag,
256           anArticleAction, aCommentAction);
257       entity.setFieldValue("priority", "1");
258       entity.setFieldValue("filter_group_id", getEntity().getId());
259       entity.insert();
260
261       Filter filter = new Filter(entity, instance);
262       introduceFilter(filter);
263
264       return "";
265     }
266
267     public String getName() {
268       return entity.getFieldValue("name");
269     }
270   }
271   
272   /** This class reflects a row of the filter database table. 
273    * To actually run a filter on data, use the test() method. 
274    * This class will automatically retreive and use the correct 
275    * filter type.
276    */
277   public class Filter {
278     private Entity entity;
279     private FilterInstance instance;
280
281     public Filter(Entity anEntity) throws AbuseExc {
282       this(anEntity, getFilterTypeForId(anEntity.getFieldValue("type")).constructFilterInstance(anEntity.getFieldValue("expression")));
283     }
284
285     public Filter(Entity anEntity, FilterInstance anInstance) {
286       entity = anEntity;
287       instance = anInstance;
288     }
289
290     public Entity getEntity() {
291       return entity;
292     }
293
294     public EntityAdapter getEntityAdapter() {
295       return model.makeEntityAdapter("filter", entity);
296     }
297
298     public void update(String aType, String anExpression, String aComments, String aTag,
299                        String anArticleAction, String aCommentAction) throws AbuseExc {
300
301       instance = getFilterTypeForId(aType).constructFilterInstance(anExpression);
302
303       entity.setFieldValue("type", aType);
304       entity.setFieldValue("expression", anExpression);
305       entity.setFieldValue("tag", aType);
306       entity.setFieldValue("comment", aComments);
307       entity.setFieldValue("articleaction", anArticleAction);
308       entity.setFieldValue("commentaction", aCommentAction);
309       entity.setFieldValue("last_hit", null);
310       entity.update();
311    }
312
313     public void updateLastHit(Date aDate) {
314       entity.setFieldValue("last_hit",
315           DatabaseHelper.convertDateToInternalRepresenation(
316               new Date(System.currentTimeMillis())));
317       entity.update();
318     }
319
320     public String getType() {
321       return entity.getFieldValue("type");
322     }
323
324     public String getExpression() {
325       return entity.getFieldValue("expression");
326     }
327
328     public String getTag() {
329       return entity.getFieldValue("tag");
330     }
331
332     public String getComment() {
333       return entity.getFieldValue("comment");
334     }
335
336     public String getArticleAction() {
337       return entity.getFieldValue("articleaction");
338     }
339
340     public String getCommentAction() {
341       return entity.getFieldValue("commentaction");
342     }
343
344     public FilterInstance getInstance() {
345       return instance;
346     }
347
348     public boolean test(Entity anEntity, Request aRequest) {
349       return instance.test(anEntity, aRequest);
350     }
351   }
352
353   public synchronized void reload() {
354     filterGroups.clear();
355     idToFilterGroup.clear();
356
357     try {
358       Iterator i = new EntityIteratorAdapter("", "priority asc", 100, model, "filterGroup");
359       while (i.hasNext()) {
360         EntityAdapter entityAdapter = (EntityAdapter) i.next();
361         List filters = new ArrayList();
362         Iterator j = (Iterator) entityAdapter.getIterator("to_filters");
363         while (j.hasNext()) {
364           filters.add(((EntityAdapter) j.next()).getEntity());
365         }
366
367         FilterGroup filterGroup = new FilterGroup(entityAdapter.getEntity(), filters);
368         introduceFilterGroup(filterGroup);
369       }
370     }
371     catch (Throwable e) {
372       logger.error("Can't load filters: " + e.getMessage());
373     }
374   }
375
376   public synchronized List getFilterGroups() {
377     List result = new ArrayList();
378     Iterator i = filterGroups.iterator();
379     while (i.hasNext()) {
380       result.add(((FilterGroup) i.next()).getEntityAdapter());
381     }
382
383     return result;
384   }
385
386   public synchronized void updateFilterGroup(String anId, String aName) {
387     FilterGroup filterGroup = getFilterGroupForId(anId);
388     filterGroup.getEntity().setFieldValue("name", aName);
389     filterGroup.getEntity().update();
390   }
391
392   public synchronized void addFilterGroup(String aName) throws DatabaseExc {
393     Entity entity = DatabaseFilterGroup.getInstance().createNewEntity();
394     entity.setFieldValue("name", aName);
395     entity.setFieldValue("priority", "1");
396     entity.insert();
397
398     FilterGroup filterGroup = new FilterGroup(entity);
399     introduceFilterGroup(filterGroup);
400   }
401
402   public synchronized void deleteFilterGroup(String anId) {
403
404     FilterGroup filterGroup = getFilterGroupForId(anId);
405     removeFilterGroup(filterGroup);
406     DatabaseFilter.getInstance().deleteByWhereClause("filter_group_id = " + anId);
407     DatabaseFilterGroup.getInstance().delete(anId);
408   }
409
410   public synchronized void deleteFilter(String aGroupId, String anId) {
411     getFilterGroupForId(aGroupId).deleteFilter(anId);
412   }
413
414
415   public synchronized String updateFilter(String aGroupId, String anId,
416                                         String aType, String anExpression,
417                                         String aComments,
418                                         String aTag,
419                                         String anArticleAction,
420                                         String aCommentAction) {
421     return getFilterGroupForId(aGroupId).updateFilter(anId, aType,
422         anExpression, aComments, aTag, anArticleAction, aCommentAction);
423   }
424
425   public synchronized String addFilter(String aGroupId,
426                                        String aType, String anExpression,
427                                        String aComments,
428                                        String aTag,
429                                        String anArticleAction,
430                                        String aCommentAction) throws DatabaseExc {
431     return getFilterGroupForId(aGroupId).createFilter(aType, anExpression,
432         aComments, aTag, anArticleAction, aCommentAction);
433   }
434
435
436   public FilterGroup getFilterGroupForId(String anId) {
437     FilterGroup result = (FilterGroup) idToFilterGroup.get(anId);
438     if (result == null) {
439       throw new NullPointerException("No such filter group");
440     }
441
442     return result;
443   }
444
445   public Filter getFilterForId(String aGroupId, String anId) {
446     return getFilterGroupForId(aGroupId).getFilterForId(anId);
447   }
448
449
450   public List getFilters(String aFilterGroupId) {
451     return getFilterGroupForId(aFilterGroupId).getFilterEntityAdapterList();
452   }
453
454   private synchronized void introduceFilterGroup(FilterGroup aFilterGroup) {
455     filterGroups.add(aFilterGroup);
456     idToFilterGroup.put(aFilterGroup.getEntity().getId(), aFilterGroup);
457   }
458
459   private synchronized void removeFilterGroup(FilterGroup aFilterGroup) {
460     filterGroups.remove(aFilterGroup);
461     idToFilterGroup.remove(aFilterGroup.getEntity().getId());
462   }
463
464   private FilterType getFilterTypeForId(String anId) {
465     return (FilterType) filterTypes.get(anId);
466   }
467 }