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