rendered paste body 1 # <@LICENSE>
2 # Copyright 2004 Apache Software Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 # </@LICENSE>
16
17 =head1 NAME
18
19 Mail::SpamAssassin::Conf::LDAP - load SpamAssassin scores from LDAP database
20
21 =head1 SYNOPSIS
22
23 (see Mail::SpamAssassin)
24
25
26 =head1 DESCRIPTION
27
28 Mail::SpamAssassin is a module to identify spam using text analysis and
29 several internet-based realtime blacklists.
30
31 This class is used internally by SpamAssassin to load scores from an LDAP
32 database. Please refer to the C<Mail::SpamAssassin> documentation for public
33 interfaces.
34
35 =head1 METHODS
36
37 =over 4
38
39 =cut
40
41 package Mail::SpamAssassin::Conf::LDAP;
42
43 use Mail::SpamAssassin::Logger;
44
45 use strict;
46 use warnings;
47 use bytes;
48 use Carp;
49
50 use vars qw{
51 @ISA
52 };
53
54 @ISA = qw();
55
56 ###########################################################################
57
58 sub new {
59 my $class = shift;
60 $class = ref($class) || $class;
61 my ($main) = @_;
62
63 my $self = {
64 'main' => $main
65 };
66
67 bless ($self, $class);
68 $self;
69 }
70
71 ###########################################################################
72
73 sub load_modules { # static
74 dbg("ldap: loading Net::LDAP and URI");
75 eval {
76 require Net::LDAP; # actual server connection
77 require URI; # parse server connection dsn
78 };
79
80 # do any other preloading that will speed up operation
81 }
82
83 ###########################################################################
84
85 =item $f->load ($username)
86
87 Read configuration paramaters from LDAP server and parse scores from it.
88
89 =cut
90
91 sub load {
92 my ($self, $username) = @_;
93
94 my $url = $self->{main}->{conf}->{user_scores_dsn}; # an ldap URI
95 dbg("ldap: URL is $url");
96 if(!defined($url) || $url eq '') {
97 dbg("ldap: No URL defined; skipping LDAP");
98 return;
99 }
100
101 eval {
102 # make sure we can see croak messages from DBI
103 local $SIG{'__DIE__'} = sub { warn "$_[0]"; };
104 require Net::LDAP;
105 require URI;
106 load_with_ldap($self, $username, $url);
107 };
108
109 if ($@) {
110 warn "ldap: failed to load user scores from LDAP server, ignored\n";
111 }
112 }
113
114 sub load_with_ldap {
115 my ($self, $username, $url) = @_;
116
117 # ldapurl = scheme "://" [hostport] ["/"
118 # [dn ["?" [attributes] ["?" [scope]
119 # ["?" [filter] ["?" extensions]]]]]]
120
121 my $uri = URI->new("$url");
122
123 my $host = $uri->host;
124 if (!defined($host) || $host eq '') {
125 dbg("ldap: No server specified, assuming localhost");
126 $host = "localhost";
127 }
128 my $port = $uri->port;
129 my $base = $uri->dn;
130 my @attr = $uri->attributes;
131 my $scope = $uri->scope;
132 my $filter = $uri->filter;
133 my %extn = $uri->extensions; # unused
134
135 $filter =~ s/__USERNAME__/$username/g;
136 dbg("ldap: host=$host, port=$port, base='$base', attr=${attr[0]}, scope=$scope, filter='$filter'");
137
138 my $main = $self->{main};
139 my $ldapuser = $main->{conf}->{user_scores_ldap_username};
140 my $ldappass = $main->{conf}->{user_scores_ldap_password};
141
142 if(!$ldapuser) {
143 undef($ldapuser);
144 } else {
145 dbg("ldap: user='$ldapuser'");
146 }
147
148 if(!$ldappass) {
149 undef($ldappass);
150 } else {
151 # don't log this to avoid leaking sensitive info
152 # dbg("ldap: pass='$ldappass'");
153 }
154
155 my $f_attribute = $attr[0];
156
157 my $ldap = Net::LDAP->new ("$host:$port", onerror => "warn");
158 if (!defined($ldapuser) && !defined($ldappass)) {
159 $ldap->bind;
160 } else {
161 $ldap->bind($ldapuser, password => $ldappass);
162 }
163
164 my $result = $ldap->search( base => $base,
165 filter => $filter,
166 scope => $scope,
167 attrs => \@attr
168 );
169
170 my $conf = '';
171 foreach my $entry ($result->all_entries) {
172 my @v = $entry->get_value($f_attribute);
173 foreach my $v (@v) {
174 dbg("ldap: retrieving prefs for $username: $v");
175 $conf .= $v."\n";
176 }
177 }
178 $main->{conf}->{main} = $main;
179 $main->{conf}->parse_scores_only($conf);
180 delete $main->{conf}->{main};
181 return;
182 }
183
184 ###########################################################################
185
186 sub sa_die { Mail::SpamAssassin::sa_die(@_); }
187
188 ###########################################################################
189
190 1;