41b0d40a26bccba3867fe665e44ae38b7ec4d374
[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.config.MirPropertiesConfiguration;
34 import mir.entity.Entity;
35 import mir.entity.adapter.EntityAdapter;
36 import mir.entity.adapter.EntityAdapterModel;
37 import mir.entity.adapter.EntityIteratorAdapter;
38 import mir.log.LoggerWrapper;
39 import mir.session.Request;
40 import mir.storage.DatabaseExc;
41 import mircoders.global.MirGlobal;
42 import mircoders.storage.DatabaseFilter;
43 import mircoders.storage.DatabaseFilterGroup;
44
45 import java.text.SimpleDateFormat;
46 import java.util.*;
47 /** The FilterEngine manages a list of all filters and filter groups.
48  *  Use the testPosting() method to apply all filters on an 
49  *  Entity (for ex. an article or a comment)
50  */
51 public class FilterEngine {
52   private Map filterTypes;
53   private List filterTypeIds;
54
55   private List filterGroups;
56   private Map idToFilterGroup;
57   private LoggerWrapper logger;
58   private EntityAdapterModel model;
59   private SimpleDateFormat dateFormat;
60   private MirPropertiesConfiguration configuration = MirPropertiesConfiguration.instance();
61
62   public FilterEngine(EntityAdapterModel aModel) {
63     logger = new LoggerWrapper("Global.Abuse.FilterEngine");
64     filterGroups = new ArrayList();
65     idToFilterGroup = new HashMap();
66
67     filterTypes = new HashMap();
68     filterTypeIds = new ArrayList();
69     try {
70       Iterator i = MirGlobal.localizer().openPostings().getAntiAbuseFilterTypes().iterator();
71       while (i.hasNext()) {
72         FilterType filterType = (FilterType) i.next();
73
74         filterTypes.put(filterType.getName(), filterType);
75         filterTypeIds.add(filterType.getName());
76       }
77     }
78     catch (Throwable t) {
79       throw new RuntimeException(t.getMessage());
80     }
81
82     model = aModel;
83
84     dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
85     dateFormat.setTimeZone(TimeZone.getTimeZone(configuration.getString("Mir.DefaultTimezone")));
86     reload();
87   }
88   /** applies all filters from all filter groups to an Entity.
89    *  The entity may be, for example, an article, or a comment.
90    *  It returns a filter that matches if it finds one, null otherwise  
91    */
92   public Filter testPosting(Entity anEntity, Request aRequest) {
93     Iterator i = filterGroups.iterator();
94     while (i.hasNext()) {
95       FilterGroup group = (FilterGroup) i.next();
96       Iterator j = group.getFilters().iterator();
97       while (j.hasNext()) {
98         Filter filter = (Filter) j.next();
99         try {
100           if (filter.test(anEntity, aRequest)) {
101             return filter;
102           }
103         }
104         catch (Throwable t) {
105           logger.warn("Exception thrown while testing filter " + filter.getType() + " ( " + filter.getExpression() + ") " + t.toString());
106         }
107       }
108     }
109
110     return null;
111   }
112
113   public List getFilterTypes() {
114     try {
115       List result = new ArrayList();
116
117       Iterator i = filterTypeIds.iterator();
118       while (i.hasNext()) {
119         String id = (String) i.next();
120
121         Map action = new HashMap();
122         action.put("resource", id);
123         action.put("identifier", id);
124
125         result.add(action);
126       }
127
128       return result;
129     }
130     catch (Throwable t) {
131       throw new RuntimeException("can't get article actions");
132     }
133   }
134   /** This class reflects a row of the filter_group 
135    * database table. Filters groups allow you to organize 
136    * your filters. For example: group1=spammer ,  
137    * group2=nazis etc.  
138    */
139   public class FilterGroup {
140     private List filters;
141     private Entity entity;
142     private Map idToFilter;
143
144     public FilterGroup(Entity anEntity) {
145       this (anEntity, Collections.EMPTY_LIST);
146     }
147
148     public FilterGroup(Entity anEntity, List aFilters) {
149       entity = anEntity;
150       filters = new ArrayList();
151       idToFilter = new HashMap();
152       Iterator i = aFilters.iterator();
153
154       while (i.hasNext()) {
155         Entity entity = (Entity) i.next();
156         try {
157           Filter filter = new Filter(entity);
158           introduceFilter(filter);
159         }
160         catch (AbuseExc e) {
161         }
162       }
163     }
164
165     public Entity getEntity() {
166       return entity;
167     }
168
169     public EntityAdapter getEntityAdapter() {
170       return model.makeEntityAdapter("filterGroup",  entity);
171     }
172
173     public List getFilterEntityAdapterList() {
174       List result = new ArrayList();
175
176       Iterator i = filters.iterator();
177       while (i.hasNext()) {
178         Filter filter = (Filter) i.next();
179         result.add(filter.getEntityAdapter());
180       }
181
182       return result;
183     }
184
185     public List getFilters() {
186       return filters;
187     }
188
189     public Filter getFilterForId(String anId) {
190       Filter result = (Filter) idToFilter.get(anId);
191       if (result==null) {
192         throw new NullPointerException("No such filter");
193       }
194
195       return result;
196     }
197
198     private void introduceFilter(Filter aFilter) {
199       filters.add(aFilter);
200       idToFilter.put(aFilter.getEntity().getId(), aFilter);
201     }
202
203     private void removeFilter(Filter aFilter) {
204       filters.remove(aFilter);
205       idToFilter.remove(aFilter.getEntity().getId());
206     }
207
208     private void deleteFilter(String anId) {
209       Filter filter = getFilterForId(anId);
210       removeFilter(filter);
211       DatabaseFilter.getInstance().delete(anId);
212     }
213
214     public void populateFilterEntity(Entity anEntity, String aType, String anExpression,
215                              String aComments, String aTag, String anArticleAction,
216                              String aCommentAction) {
217
218       anEntity.setFieldValue("type", aType);
219       anEntity.setFieldValue("expression", anExpression);
220       anEntity.setFieldValue("comment", aComments);
221       anEntity.setFieldValue("tag", aTag);
222       anEntity.setFieldValue("articleaction", anArticleAction);
223       anEntity.setFieldValue("commentaction", aCommentAction);
224     }
225
226     public String updateFilter(String anId, String aType, String anExpression,
227                              String aComments, String aTag, String anArticleAction,
228                              String aCommentAction) {
229
230       try {
231         getFilterTypeForId(aType).constructFilterInstance(anExpression);
232       }
233       catch (AbuseExc e) {
234         return e.getMessage();
235       }
236
237       Entity entity = getFilterForId(anId).getEntity();
238       populateFilterEntity(entity, aType, anExpression, aComments, aTag,
239           anArticleAction, aCommentAction);
240       entity.update();
241
242       return "";
243     }
244
245     public String createFilter(String aType, String anExpression,
246                              String aComments, String aTag, String anArticleAction,
247                              String aCommentAction) throws DatabaseExc {
248       FilterInstance instance;
249
250       try {
251         instance = getFilterTypeForId(aType).constructFilterInstance(anExpression);
252       }
253       catch (AbuseExc e) {
254         return e.getMessage();
255       }
256
257       Entity entity = DatabaseFilter.getInstance().createNewEntity();
258       populateFilterEntity(entity, aType, anExpression, aComments, aTag,
259           anArticleAction, aCommentAction);
260       entity.setFieldValue("priority", "1");
261       entity.setFieldValue("filter_group_id", getEntity().getId());
262       entity.insert();
263
264       Filter filter = new Filter(entity, instance);
265       introduceFilter(filter);
266
267       return "";
268     }
269
270     public String getName() {
271       return entity.getFieldValue("name");
272     }
273   }
274   
275   /** This class reflects a row of the filter database table. 
276    * To actually run a filter on data, use the test() method. 
277    * This class will automatically retreive and use the correct 
278    * filter type.
279    */
280   public class Filter {
281     private Entity entity;
282     private FilterInstance instance;
283
284     public Filter(Entity anEntity) throws AbuseExc {
285       this(anEntity, getFilterTypeForId(anEntity.getFieldValue("type")).constructFilterInstance(anEntity.getFieldValue("expression")));
286     }
287
288     public Filter(Entity anEntity, FilterInstance anInstance) {
289       entity = anEntity;
290       instance = anInstance;
291     }
292
293     public Entity getEntity() {
294       return entity;
295     }
296
297     public EntityAdapter getEntityAdapter() {
298       return model.makeEntityAdapter("filter", entity);
299     }
300
301     public void update(String aType, String anExpression, String aComments, String aTag,
302                        String anArticleAction, String aCommentAction) throws AbuseExc {
303
304       instance = getFilterTypeForId(aType).constructFilterInstance(anExpression);
305
306       entity.setFieldValue("type", aType);
307       entity.setFieldValue("expression", anExpression);
308       entity.setFieldValue("tag", aType);
309       entity.setFieldValue("comment", aComments);
310       entity.setFieldValue("articleaction", anArticleAction);
311       entity.setFieldValue("commentaction", aCommentAction);
312       entity.setFieldValue("last_hit", null);
313       entity.update();
314    }
315
316     public void updateLastHit(Date aDate) {
317       entity.setFieldValue("last_hit", dateFormat.format(aDate));
318       entity.update();
319     }
320
321     public String getType() {
322       return entity.getFieldValue("type");
323     }
324
325     public String getExpression() {
326       return entity.getFieldValue("expression");
327     }
328
329     public String getTag() {
330       return entity.getFieldValue("tag");
331     }
332
333     public String getComment() {
334       return entity.getFieldValue("comment");
335     }
336
337     public String getArticleAction() {
338       return entity.getFieldValue("articleaction");
339     }
340
341     public String getCommentAction() {
342       return entity.getFieldValue("commentaction");
343     }
344
345     public FilterInstance getInstance() {
346       return instance;
347     }
348
349     public boolean test(Entity anEntity, Request aRequest) {
350       return instance.test(anEntity, aRequest);
351     }
352   }
353
354   public synchronized void reload() {
355     filterGroups.clear();
356     idToFilterGroup.clear();
357
358     try {
359       Iterator i = new EntityIteratorAdapter("", "priority asc", 100, model, "filterGroup");
360       while (i.hasNext()) {
361         EntityAdapter entityAdapter = (EntityAdapter) i.next();
362         List filters = new ArrayList();
363         Iterator j = (Iterator) entityAdapter.get("to_filters");
364         while (j.hasNext()) {
365           filters.add(((EntityAdapter) j.next()).getEntity());
366         }
367
368         FilterGroup filterGroup = new FilterGroup(entityAdapter.getEntity(), filters);
369         introduceFilterGroup(filterGroup);
370       }
371     }
372     catch (Throwable e) {
373       logger.error("Can't load filters: " + e.getMessage());
374     }
375   }
376
377   public synchronized List getFilterGroups() {
378     List result = new ArrayList();
379     Iterator i = filterGroups.iterator();
380     while (i.hasNext()) {
381       result.add(((FilterGroup) i.next()).getEntityAdapter());
382     }
383
384     return result;
385   }
386
387   public synchronized void updateFilterGroup(String anId, String aName) {
388     FilterGroup filterGroup = getFilterGroupForId(anId);
389     filterGroup.getEntity().setFieldValue("name", aName);
390     filterGroup.getEntity().update();
391   }
392
393   public synchronized void addFilterGroup(String aName) throws DatabaseExc {
394     Entity entity = DatabaseFilterGroup.getInstance().createNewEntity();
395     entity.setFieldValue("name", aName);
396     entity.setFieldValue("priority", "1");
397     entity.insert();
398
399     FilterGroup filterGroup = new FilterGroup(entity);
400     introduceFilterGroup(filterGroup);
401   }
402
403   public synchronized void deleteFilterGroup(String anId) {
404
405     FilterGroup filterGroup = getFilterGroupForId(anId);
406     removeFilterGroup(filterGroup);
407     DatabaseFilter.getInstance().deleteByWhereClause("filter_group_id = " + anId);
408     DatabaseFilterGroup.getInstance().delete(anId);
409   }
410
411   public synchronized void deleteFilter(String aGroupId, String anId) {
412     getFilterGroupForId(aGroupId).deleteFilter(anId);
413   }
414
415
416   public synchronized String updateFilter(String aGroupId, String anId,
417                                         String aType, String anExpression,
418                                         String aComments,
419                                         String aTag,
420                                         String anArticleAction,
421                                         String aCommentAction) {
422     return getFilterGroupForId(aGroupId).updateFilter(anId, aType,
423         anExpression, aComments, aTag, anArticleAction, aCommentAction);
424   }
425
426   public synchronized String addFilter(String aGroupId,
427                                        String aType, String anExpression,
428                                        String aComments,
429                                        String aTag,
430                                        String anArticleAction,
431                                        String aCommentAction) throws DatabaseExc {
432     return getFilterGroupForId(aGroupId).createFilter(aType, anExpression,
433         aComments, aTag, anArticleAction, aCommentAction);
434   }
435
436
437   public FilterGroup getFilterGroupForId(String anId) {
438     FilterGroup result = (FilterGroup) idToFilterGroup.get(anId);
439     if (result == null) {
440       throw new NullPointerException("No such filter group");
441     }
442
443     return result;
444   }
445
446   public Filter getFilterForId(String aGroupId, String anId) {
447     return getFilterGroupForId(aGroupId).getFilterForId(anId);
448   }
449
450
451   public List getFilters(String aFilterGroupId) {
452     return getFilterGroupForId(aFilterGroupId).getFilterEntityAdapterList();
453   }
454
455   private void introduceFilterGroup(FilterGroup aFilterGroup) {
456     filterGroups.add(aFilterGroup);
457     idToFilterGroup.put(aFilterGroup.getEntity().getId(), aFilterGroup);
458   }
459
460   private void removeFilterGroup(FilterGroup aFilterGroup) {
461     filterGroups.remove(aFilterGroup);
462     idToFilterGroup.remove(aFilterGroup.getEntity().getId());
463   }
464
465   private FilterType getFilterTypeForId(String anId) {
466     return (FilterType) filterTypes.get(anId);
467   }
468 }