File Coverage

blib/lib/Mail/SpamAssassin/Conf/SQL.pm
Criterion Covered Total %
statement 12 85 14.1
branch 0 22 0.0
condition 0 6 0.0
subroutine 4 10 40.0
pod 1 5 20.0
total 17 128 13.2


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             =head1 NAME
19              
20             Mail::SpamAssassin::Conf::SQL - load SpamAssassin scores from SQL database
21              
22             =head1 SYNOPSIS
23              
24             (see Mail::SpamAssassin)
25            
26              
27             =head1 DESCRIPTION
28              
29             Mail::SpamAssassin is a module to identify spam using text analysis and
30             several internet-based realtime blacklists.
31              
32             This class is used internally by SpamAssassin to load scores from an SQL
33             database. Please refer to the C<Mail::SpamAssassin> documentation for public
34             interfaces.
35              
36             =head1 METHODS
37              
38             =over 4
39              
40             =cut
41              
42             package Mail::SpamAssassin::Conf::SQL;
43              
44 40     40   287 use Mail::SpamAssassin::Logger;
  40         159  
  40         2703  
45              
46 40     40   266 use strict;
  40         91  
  40         882  
47 40     40   219 use warnings;
  40         75  
  40         1227  
48             # use bytes;
49 40     40   226 use re 'taint';
  40         83  
  40         29003  
50              
51             our @ISA = qw();
52              
53             ###########################################################################
54              
55             sub new {
56 0     0 0   my $class = shift;
57 0   0       $class = ref($class) || $class;
58 0           my ($main) = @_;
59              
60 0           my $self = {
61             'main' => $main
62             };
63              
64 0           bless ($self, $class);
65 0           $self;
66             }
67              
68             ###########################################################################
69              
70             sub load_modules { # static
71 0     0 0   eval {
72 0           require DBI;
73             };
74              
75             # do any other preloading that will speed up operation
76             }
77              
78             ###########################################################################
79              
80             =item $f->load ($username)
81              
82             Read configuration parameters from SQL database and parse scores from it.
83              
84             =back
85              
86             =cut
87              
88             sub load {
89 0     0 1   my ($self, $username) = @_;
90              
91 0           my $conf = $self->{main}->{conf};
92 0           my $dsn = $conf->{user_scores_dsn};
93 0 0 0       if (!defined($dsn) || $dsn eq '') {
94 0           dbg("config: no DSN defined; skipping sql");
95 0           return 1;
96             }
97              
98             eval {
99             # make sure we can see croak messages from DBI
100 0     0     local $SIG{'__DIE__'} = sub { die "$_[0]"; };
  0            
101 0           require DBI;
102 0           load_with_dbi($self, $username, $dsn);
103 0           1;
104 0 0         } or do {
105 0 0         my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
  0            
106 0 0         if ($conf->{user_scores_fail_to_global}) {
107 0           info("config: failed to load user (%s) scores from SQL database, ".
108             "using a global default: %s", $username, $eval_stat);
109 0           return 1;
110             } else {
111 0           warn sprintf(
112             "config: failed to load user (%s) scores from SQL database: %s\n",
113             $username, $eval_stat);
114 0           return 0;
115             }
116             };
117 0           return 1;
118             }
119              
120             sub load_with_dbi {
121 0     0 0   my ($self, $username, $dsn) = @_;
122              
123 0           my $main = $self->{main};
124 0           my $conf = $main->{conf};
125 0           my $dbuser = $conf->{user_scores_sql_username};
126 0           my $dbpass = $conf->{user_scores_sql_password};
127 0           my $custom_query = $conf->{user_scores_sql_custom_query};
128              
129 0           my $f_preference = 'preference';
130 0           my $f_value = 'value';
131 0           my $f_username = 'username';
132 0           my $f_table = 'userpref';
133              
134 0           my $dbh = DBI->connect($dsn, $dbuser, $dbpass, {'PrintError' => 0});
135              
136 0 0         if ($dbh) {
137 0           my $sql;
138 0 0         if (defined($custom_query)) {
139 0           $sql = $custom_query;
140 0           my $quoted_username = $dbh->quote($username);
141 0           my ($mailbox, $domain) = split('@', $username);
142 0           my $quoted_mailbox = $dbh->quote($mailbox);
143 0           my $quoted_domain = $dbh->quote($domain);
144              
145 0           $sql =~ s/_USERNAME_/$quoted_username/g;
146 0           $sql =~ s/_TABLE_/$f_table/g;
147 0           $sql =~ s/_MAILBOX_/$quoted_mailbox/g;
148 0           $sql =~ s/_DOMAIN_/$quoted_domain/g;
149             }
150             else {
151 0           $sql = "select $f_preference, $f_value from $f_table where ".
152             "$f_username = ".$dbh->quote($username).
153             " or $f_username = '\@GLOBAL' order by $f_username asc";
154             }
155 0           dbg("config: Conf::SQL: executing SQL: $sql");
156 0           my $sth = $dbh->prepare($sql);
157 0 0         if ($sth) {
158 0           my $rv = $sth->execute();
159 0 0         if ($rv) {
160 0           dbg("config: retrieving prefs for $username from SQL server");
161 0           my @row;
162 0           my $config_text = '';
163 0           while (@row = $sth->fetchrow_array()) {
164 0 0         $config_text .= (defined($row[0]) ? $row[0] : '') . "\t" .
    0          
165             (defined($row[1]) ? $row[1] : '') . "\n";
166             }
167 0 0         if ($config_text ne '') {
168 0           $conf->{main} = $main;
169 0           $conf->parse_scores_only($config_text);
170 0           delete $conf->{main};
171             }
172 0           $sth->finish();
173 0           undef $sth;
174             }
175             else {
176 0           die "config: SQL error: $sql\n".$sth->errstr."\n";
177             }
178             }
179             else {
180 0           die "config: SQL error: " . $dbh->errstr . "\n";
181             }
182 0           $dbh->disconnect();
183             }
184             else {
185 0           die "config: SQL error: " . DBI->errstr . "\n";
186             }
187             }
188              
189             ###########################################################################
190              
191 0     0 0   sub sa_die { Mail::SpamAssassin::sa_die(@_); }
192              
193             ###########################################################################
194              
195             1;