]>
Commit | Line | Data |
---|---|---|
c881fe35 DM |
1 | package PMG::RuleCache; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | use DBI; | |
c881fe35 DM |
6 | |
7 | use PVE::SafeSyslog; | |
8 | ||
9 | use PMG::Utils; | |
10 | use PMG::RuleDB; | |
11 | use Digest::SHA; | |
12 | ||
13 | my $ocache_size = 1023; | |
14 | ||
15 | sub new { | |
16 | my ($type, $ruledb) = @_; | |
17 | ||
18 | my $self; | |
19 | ||
20 | $self->{ruledb} = $ruledb; | |
21 | $self->{ocache} = (); | |
22 | ||
23 | bless $self, $type; | |
24 | ||
25 | my $rules = (); | |
26 | ||
27 | my $dbh = $ruledb->{dbh}; | |
28 | ||
29 | my $sha1 = Digest::SHA->new; | |
30 | ||
31 | eval { | |
32 | $dbh->begin_work; | |
33 | ||
34 | # read a consistent snapshot | |
35 | $dbh->do("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"); | |
36 | ||
37 | my $sth = $dbh->prepare( | |
38 | "SELECT ID, Name, Priority, Active, Direction FROM Rule " . | |
39 | "where Active > 0 " . | |
40 | "ORDER BY Priority DESC"); | |
41 | ||
42 | $sth->execute(); | |
43 | ||
44 | while (my $ref = $sth->fetchrow_hashref()) { | |
45 | my $ruleid = $ref->{id}; | |
46 | my $rule = PMG::RuleDB::Rule->new( | |
47 | $ref->{name}, $ref->{priority}, $ref->{active}, | |
48 | $ref->{direction}); | |
49 | ||
50 | $rule->{id} = $ruleid; | |
51 | push @$rules, $rule; | |
52 | ||
53 | $sha1->add(join (',', values (%$ref)) . "|"); | |
54 | ||
55 | my ($from, $to, $when, $what, $action); | |
56 | ||
57 | my $sth1 = $dbh->prepare( | |
58 | "SELECT Objectgroup_ID, Grouptype FROM RuleGroup " . | |
59 | "where RuleGroup.Rule_ID = '$ruleid' " . | |
60 | "ORDER BY Grouptype, Objectgroup_ID"); | |
61 | ||
62 | $sth1->execute(); | |
63 | while (my $ref1 = $sth1->fetchrow_hashref()) { | |
64 | my $gtype = $ref1->{grouptype}; | |
65 | my $groupid = $ref1->{objectgroup_id}; | |
66 | ||
67 | # emtyp groups differ from non-existent groups! | |
68 | ||
69 | if ($gtype == 0) { #from | |
70 | $from = [] if !defined ($from); | |
71 | } elsif ($gtype == 1) { # to | |
72 | $to = [] if !defined ($to); | |
73 | } elsif ($gtype == 2) { # when | |
74 | $when = [] if !defined ($when); | |
75 | } elsif ($gtype == 3) { # what | |
76 | $what = [] if !defined ($what); | |
77 | } elsif ($gtype == 4) { # action | |
78 | $action = [] if !defined ($action); | |
79 | } | |
80 | ||
81 | my $sth2 = $dbh->prepare( | |
82 | "SELECT ID FROM Object where Objectgroup_ID = '$groupid' " . | |
83 | "ORDER BY ID"); | |
84 | $sth2->execute(); | |
85 | while (my $ref2 = $sth2->fetchrow_hashref()) { | |
86 | my $objid = $ref2->{'id'}; | |
87 | my $obj = $self->_get_object($objid); | |
88 | ||
89 | $sha1->add (join (',', $objid, $gtype, $groupid) . "|"); | |
90 | $sha1->add ($obj->{digest}, "|"); | |
91 | ||
92 | if ($gtype == 0) { #from | |
93 | push @$from, $obj; | |
94 | } elsif ($gtype == 1) { # to | |
95 | push @$to, $obj; | |
96 | } elsif ($gtype == 2) { # when | |
97 | push @$when, $obj; | |
98 | } elsif ($gtype == 3) { # what | |
99 | push @$what, $obj; | |
100 | if ($obj->otype == PMG::RuleDB::ArchiveFilter->otype) { | |
101 | if ($rule->{direction} == 0) { | |
102 | $self->{archivefilter_in} = 1; | |
103 | } elsif ($rule->{direction} == 1) { | |
104 | $self->{archivefilter_out} = 1; | |
105 | } else { | |
106 | $self->{archivefilter_in} = 1; | |
107 | $self->{archivefilter_out} = 1; | |
108 | } | |
109 | } | |
110 | } elsif ($gtype == 4) { # action | |
111 | push @$action, $obj; | |
112 | $self->{"$ruleid:final"} = 1 if $obj->final(); | |
113 | } | |
114 | } | |
115 | $sth2->finish(); | |
116 | } | |
117 | ||
118 | $sth1->finish(); | |
119 | ||
120 | $self->{"$ruleid:from"} = $from; | |
121 | $self->{"$ruleid:to"} = $to; | |
122 | $self->{"$ruleid:when"} = $when; | |
123 | $self->{"$ruleid:what"} = $what; | |
124 | $self->{"$ruleid:action"} = $action; | |
125 | } | |
126 | ||
127 | # Cache Greylist Exclusion | |
128 | $sth = $dbh->prepare( | |
129 | "SELECT object.id FROM object, objectgroup " . | |
130 | "WHERE class = 'greylist' AND " . | |
131 | "objectgroup.id = object.objectgroup_id " . | |
132 | "ORDER BY object.id"); | |
133 | ||
134 | $sth->execute(); | |
135 | my $grey_excl_sender = (); | |
136 | my $grey_excl_receiver = (); | |
137 | while (my $ref2 = $sth->fetchrow_hashref()) { | |
138 | my $obj = $self->_get_object ($ref2->{'id'}); | |
139 | ||
140 | if ($obj->receivertest()) { | |
141 | push @$grey_excl_receiver, $obj; | |
142 | } else { | |
143 | push @$grey_excl_sender, $obj; | |
144 | } | |
145 | $sha1->add (join (',', values (%$ref2)) . "|"); | |
146 | $sha1->add ($obj->{digest}, "|"); | |
147 | } | |
148 | ||
149 | $self->{"greylist:sender"} = $grey_excl_sender; | |
150 | $self->{"greylist:receiver"} = $grey_excl_receiver; | |
151 | ||
152 | $sth->finish(); | |
153 | }; | |
154 | my $err = $@; | |
155 | ||
156 | $dbh->rollback; # end transaction | |
157 | ||
158 | syslog ('err', PMG::Utils::msgquote("unable to load rulecache : $err")) if $err; | |
159 | ||
160 | $self->{rules} = $rules; | |
161 | ||
162 | $self->{digest} = $sha1->hexdigest; | |
163 | ||
164 | return $self; | |
165 | } | |
166 | ||
167 | sub final { | |
168 | my ($self, $ruleid) = @_; | |
169 | ||
9ef3f143 | 170 | defined($ruleid) || die "undefined rule id: ERROR"; |
c881fe35 DM |
171 | |
172 | return $self->{"$ruleid:final"}; | |
173 | } | |
174 | ||
175 | sub rules { | |
176 | my ($self) = @_; | |
177 | ||
178 | $self->{rules}; | |
179 | } | |
180 | ||
181 | sub _get_object { | |
182 | my ($self, $objid) = @_; | |
183 | ||
184 | my $cid = $objid % $ocache_size; | |
185 | ||
186 | my $obj = $self->{ocache}[$cid]; | |
187 | ||
188 | if (!defined ($obj) || $obj->{id} != $objid) { | |
189 | $obj = $self->{ruledb}->load_object($objid); | |
190 | $self->{ocache}[$cid] = $obj; | |
191 | } | |
192 | ||
9ef3f143 | 193 | $obj || die "unable to get object $objid: ERROR"; |
c881fe35 DM |
194 | |
195 | return $obj; | |
196 | } | |
197 | ||
198 | sub get_actions { | |
199 | my ($self, $ruleid) = @_; | |
200 | ||
9ef3f143 | 201 | defined($ruleid) || die "undefined rule id: ERROR"; |
c881fe35 DM |
202 | |
203 | return $self->{"$ruleid:action"}; | |
204 | } | |
205 | ||
206 | sub greylist_match { | |
207 | my ($self, $addr, $ip) = @_; | |
208 | ||
209 | my $grey = $self->{"greylist:sender"}; | |
210 | ||
211 | foreach my $obj (@$grey) { | |
212 | if ($obj->who_match ($addr, $ip)) { | |
213 | return 1; | |
214 | } | |
215 | } | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
220 | sub greylist_match_receiver { | |
221 | my ($self, $addr) = @_; | |
222 | ||
223 | my $grey = $self->{"greylist:receiver"}; | |
224 | ||
225 | foreach my $obj (@$grey) { | |
226 | if ($obj->who_match($addr)) { | |
227 | return 1; | |
228 | } | |
229 | } | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
234 | sub from_match { | |
235 | my ($self, $ruleid, $addr, $ip, $ldap) = @_; | |
236 | ||
237 | my $from = $self->{"$ruleid:from"}; | |
238 | ||
239 | return 1 if !defined ($from); | |
240 | ||
241 | foreach my $obj (@$from) { | |
242 | return 1 if $obj->who_match($addr, $ip, $ldap); | |
243 | } | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
248 | sub to_match { | |
249 | my ($self, $ruleid, $addr, $ldap) = @_; | |
250 | ||
251 | my $to = $self->{"$ruleid:to"}; | |
252 | ||
253 | return 1 if !defined ($to); | |
254 | ||
255 | foreach my $obj (@$to) { | |
256 | return 1 if $obj->who_match($addr, undef, $ldap); | |
257 | } | |
258 | ||
259 | return 0; | |
260 | } | |
261 | ||
262 | sub when_match { | |
263 | my ($self, $ruleid, $time) = @_; | |
264 | ||
265 | my $when = $self->{"$ruleid:when"}; | |
266 | ||
267 | return 1 if !defined ($when); | |
268 | ||
269 | foreach my $obj (@$when) { | |
270 | return 1 if $obj->when_match($time); | |
271 | } | |
272 | ||
273 | return 0; | |
274 | } | |
275 | ||
276 | sub what_match { | |
277 | my ($self, $ruleid, $queue, $element, $msginfo, $dbh) = @_; | |
278 | ||
279 | my $what = $self->{"$ruleid:what"}; | |
280 | ||
281 | my $res; | |
282 | ||
283 | # $res->{marks} is used by mark specific actions like remove-attachments | |
284 | # $res->{$target}->{marks} is only used in apply_rules() to exclude some | |
285 | # targets (spam blacklist and whitelist) | |
286 | ||
287 | if (!defined ($what)) { | |
288 | # match all targets | |
289 | foreach my $target (@{$msginfo->{targets}}) { | |
290 | $res->{$target}->{marks} = []; | |
291 | } | |
292 | ||
293 | $res->{marks} = []; | |
294 | return $res; | |
295 | } | |
296 | ||
297 | my $marks; | |
298 | ||
299 | foreach my $obj (@$what) { | |
300 | if (!$obj->can('what_match_targets')) { | |
301 | if (my $match = $obj->what_match($queue, $element, $msginfo, $dbh)) { | |
302 | push @$marks, @$match; | |
303 | } | |
304 | } | |
305 | } | |
306 | ||
307 | foreach my $target (@{$msginfo->{targets}}) { | |
308 | $res->{$target}->{marks} = $marks; | |
309 | $res->{marks} = $marks; | |
310 | } | |
311 | ||
312 | foreach my $obj (@$what) { | |
313 | if ($obj->can ("what_match_targets")) { | |
314 | my $target_info; | |
315 | if ($target_info = $obj->what_match_targets($queue, $element, $msginfo, $dbh)) { | |
316 | foreach my $k (keys %$target_info) { | |
317 | my $cmarks = $target_info->{$k}->{marks}; # make a copy | |
318 | $res->{$k} = $target_info->{$k}; | |
319 | push @{$res->{$k}->{marks}}, @$cmarks if $cmarks; | |
320 | } | |
321 | } | |
322 | } | |
323 | } | |
324 | ||
325 | return $res; | |
326 | } | |
327 | ||
328 | 1; |