File Coverage

blib/lib/Mail/SpamAssassin/DBBasedAddrList.pm
Criterion Covered Total %
statement 72 90 80.0
branch 6 18 33.3
condition 9 14 64.2
subroutine 12 13 92.3
pod 6 6 100.0
total 105 141 74.4


line stmt bran cond sub pod time code
1             # <@LICENSE>
2             # Licensed to the Apache Software Foundation (ASF) under one or more
3             # contributor license agreements. See the NOTICE file distributed with
4             # this work for additional information regarding copyright ownership.
5             # The ASF licenses this file to you under the Apache License, Version 2.0
6             # (the "License"); you may not use this file except in compliance with
7             # the License. You may obtain a copy of the License at:
8             #
9             # http://www.apache.org/licenses/LICENSE-2.0
10             #
11             # Unless required by applicable law or agreed to in writing, software
12             # distributed under the License is distributed on an "AS IS" BASIS,
13             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14             # See the License for the specific language governing permissions and
15             # limitations under the License.
16             # </@LICENSE>
17              
18              
19             use strict;
20 6     6   41 use warnings;
  6         13  
  6         4582  
21 6     6   33 # use bytes;
  6         14  
  6         256  
22             use re 'taint';
23 6     6   40 use Fcntl;
  6         10  
  6         247  
24 6     6   30  
  6         16  
  6         1659  
25             use Mail::SpamAssassin::PersistentAddrList;
26 6     6   1833 use Mail::SpamAssassin::Util qw(untaint_var);
  6         16  
  6         176  
27 6     6   40 use Mail::SpamAssassin::Logger;
  6         14  
  6         252  
28 6     6   37  
  6         11  
  6         5002  
29             our @ISA = qw(Mail::SpamAssassin::PersistentAddrList);
30              
31             ###########################################################################
32              
33             my $class = shift;
34             $class = ref($class) || $class;
35 7     7 1 18 my $self = $class->SUPER::new(@_);
36 7   33     47 $self->{class} = $class;
37 7         40 bless ($self, $class);
38 7         31 $self;
39 7         14 }
40 7         110  
41             ###########################################################################
42              
43             my ($factory, $main) = @_;
44             my $class = $factory->{class};
45              
46 10     10 1 20 my $self = {
47 10         21 'main' => $main,
48             'accum' => { },
49 10         43 'is_locked' => 0,
50             'locked_file' => ''
51             };
52              
53             my @order = split(/\s+/, $main->{conf}->{auto_whitelist_db_modules});
54             untaint_var(\@order);
55             my $dbm_module = Mail::SpamAssassin::Util::first_available_module (@order);
56 10         60 if (!$dbm_module) {
57 10         46 die "auto-whitelist: cannot find a usable DB package from auto_whitelist_db_modules: " .
58 10         42 $main->{conf}->{auto_whitelist_db_modules}."\n";
59 10 50       30 }
60              
61 0         0 my $umask = umask ~ (oct($main->{conf}->{auto_whitelist_file_mode}));
62              
63             # if undef then don't worry -- empty hash!
64 10         92 if (defined($main->{conf}->{auto_whitelist_path})) {
65             my $path = $main->sed_path($main->{conf}->{auto_whitelist_path});
66             my ($mod1, $mod2);
67 10 50       61  
68 10         66 if ($main->{locker}->safe_lock
69 10         33 ($path, 30, $main->{conf}->{auto_whitelist_file_mode}))
70             {
71 10 50       68 $self->{locked_file} = $path;
72             $self->{is_locked} = 1;
73             ($mod1, $mod2) = ('R/W', O_RDWR | O_CREAT);
74 10         30 }
75 10         21 else {
76 10         21 $self->{is_locked} = 0;
77             ($mod1, $mod2) = ('R/O', O_RDONLY);
78             }
79 0         0  
80 0         0 dbg("auto-whitelist: tie-ing to DB file of type $dbm_module $mod1 in $path");
81              
82             ($self->{is_locked} && $dbm_module eq 'DB_File') and
83 10         56 Mail::SpamAssassin::Util::avoid_db_file_locking_bug($path);
84              
85 10 50 33     93 if (! tie %{ $self->{accum} }, $dbm_module, $path, $mod2,
86             oct($main->{conf}->{auto_whitelist_file_mode}) & 0666)
87             {
88 10 50       19 my $err = $!; # might get overwritten later
  10         752  
89             if ($self->{is_locked}) {
90             $self->{main}->{locker}->safe_unlock($self->{locked_file});
91 0         0 $self->{is_locked} = 0;
92 0 0       0 }
93 0         0 die "auto-whitelist: cannot open auto_whitelist_path $path: $err\n";
94 0         0 }
95             }
96 0         0 umask $umask;
97              
98             bless ($self, $class);
99 10         67 return $self;
100             }
101 10         48  
102 10         58 ###########################################################################
103              
104             my $self = shift;
105             dbg("auto-whitelist: DB addr list: untie-ing and unlocking");
106             untie %{$self->{accum}};
107             if ($self->{is_locked}) {
108 10     10 1 15 dbg("auto-whitelist: DB addr list: file locked, breaking lock");
109 10         31 $self->{main}->{locker}->safe_unlock ($self->{locked_file});
110 10         16 $self->{is_locked} = 0;
  10         189  
111 10 50       46 }
112 10         32 # TODO: untrap signals to unlock the db file here
113 10         54 }
114 10         34  
115             ###########################################################################
116              
117             my ($self, $addr, $signedby) = @_;
118              
119             my $entry = {
120             addr => $addr,
121             };
122 11     11 1 31  
123             $entry->{msgcount} = $self->{accum}->{$addr} || 0;
124 11         33 $entry->{totscore} = $self->{accum}->{$addr.'|totscore'} || 0;
125              
126             dbg("auto-whitelist: db-based $addr scores ".$entry->{msgcount}.'/'.$entry->{totscore});
127             return $entry;
128 11   100     296 }
129 11   100     110  
130             ###########################################################################
131 11         103  
132 11         35 my($self, $entry, $score) = @_;
133              
134             $entry->{msgcount} ||= 0;
135             $entry->{addr} ||= '';
136              
137             $entry->{msgcount}++;
138 8     8 1 27 $entry->{totscore} += $score;
139              
140 8   100     46 dbg("auto-whitelist: add_score: new count: ".$entry->{msgcount}.", new totscore: ".$entry->{totscore});
141 8   50     33  
142             $self->{accum}->{$entry->{addr}} = $entry->{msgcount};
143 8         18 $self->{accum}->{$entry->{addr}.'|totscore'} = $entry->{totscore};
144 8         26 return $entry;
145             }
146 8         70  
147             ###########################################################################
148 8         257  
149 8         163 my ($self, $entry) = @_;
150 8         44  
151             my $addr = $entry->{addr};
152             delete $self->{accum}->{$addr};
153             delete $self->{accum}->{$addr.'|totscore'};
154              
155             if ($addr =~ /^(.*)\|ip=none$/) {
156 0     0 1   # it doesn't have an IP attached.
157             # try to delete any per-IP entries for this addr as well.
158 0           # could be slow...
159 0           my $mailaddr = $1;
160 0            
161             while (my ($key, $value) = each %{$self->{accum}}) {
162 0 0         # regex will catch both key and key|totscore entries and delete them
163             if ($key =~ /^\Q${mailaddr}\E\|/) {
164             delete $self->{accum}->{$key};
165             }
166 0           }
167             }
168 0           }
  0            
169              
170 0 0         ###########################################################################
171 0            
172             1;