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
20Mail::SpamAssassin::Bayes - support for learning classifiers
21
22=head1 DESCRIPTION
23
24This is the general class used to train a learning classifier with new samples
25of spam and ham mail, and classify based on prior training.
26
27Prior to version 3.3.0, the default Bayes implementation was here; if you're
28looking for information on that, it has moved to
29C<Mail::SpamAssassin::Plugin::Bayes>.
30
31=cut
32
33package Mail::SpamAssassin::Bayes;
34
35use strict;
36use warnings;
37# use bytes;
38use re 'taint';
39
40use Mail::SpamAssassin;
41use Mail::SpamAssassin::PerMsgStatus;
42use Mail::SpamAssassin::Logger;
43use Mail::SpamAssassin::Util qw(untaint_var);
44
45our @ISA = qw();
46
47###########################################################################
48
49sub new {
50  my $class = shift;
51  $class = ref($class) || $class;
52
53  my ($main) = @_;
54  my $self = {
55    'main'              => $main,
56    'conf'		=> $main->{conf},
57    'use_ignores'       => 1,
58  };
59  bless ($self, $class);
60
61  $self->{main}->call_plugins("learner_new");
62  $self;
63}
64
65###########################################################################
66
67sub finish {
68  my $self = shift;
69  # we don't need to do the plugin; Mail::SpamAssassin::finish() does
70  # that for us
71  %{$self} = ();
72}
73
74###########################################################################
75
76# force the Bayes dbs to be closed, if they haven't already been; called
77# at the end of scan operation, or when switching between user IDs,
78# or when C<Mail::SpamAssassin::finish_learner()> is called.
79#
80sub force_close {
81  my $self = shift;
82  my $quiet = shift;
83  $self->{main}->call_plugins("learner_close", { quiet => $quiet });
84}
85
86###########################################################################
87
88sub ignore_message {
89  my ($self,$PMS) = @_;
90
91  return 0 unless $self->{use_ignores};
92
93  my $ig_from = $self->{main}->call_plugins ("check_wb_list",
94        { permsgstatus => $PMS, type => 'from', list => 'bayes_ignore_from' });
95  my $ig_to = $self->{main}->call_plugins ("check_wb_list",
96        { permsgstatus => $PMS, type => 'to', list => 'bayes_ignore_to' });
97
98  my $ignore = $ig_from || $ig_to;
99  dbg("bayes: not using bayes, bayes_ignore_from or _to rule") if $ignore;
100  return $ignore;
101}
102
103###########################################################################
104
105sub learn {
106  my ($self, $isspam, $msg, $id) = @_;
107  return unless $self->{conf}->{use_learner};
108  return unless defined $msg;
109
110  if( $self->{use_ignores} )  # Remove test when PerMsgStatus available.
111  {
112    # DMK, koppel@ece.lsu.edu:  Hoping that the ultimate fix to bug 2263 will
113    # make it unnecessary to construct a PerMsgStatus here.
114    my $PMS = Mail::SpamAssassin::PerMsgStatus->new($self->{main}, $msg);
115    my $ignore = $self->ignore_message($PMS);
116    $PMS->finish();
117    return 0 if $ignore;
118  }
119
120  return $self->{main}->call_plugins("learn_message", { isspam => $isspam, msg => $msg, id => $id });
121}
122
123###########################################################################
124
125sub forget {
126  my ($self, $msg, $id) = @_;
127  return unless $self->{conf}->{use_learner};
128  return unless defined $msg;
129  return $self->{main}->call_plugins("forget_message", { msg => $msg, id => $id });
130}
131
132###########################################################################
133
134sub sync {
135  my ($self, $sync, $expire, $opts) = @_;
136  return 0 unless $self->{conf}->{use_learner};
137
138  if ($sync) {
139    $self->{main}->call_plugins("learner_sync", $opts );
140  }
141  if ($expire) {
142    $self->{main}->call_plugins("learner_expire_old_training", $opts );
143  }
144
145  return 0;
146}
147
148###########################################################################
149
150sub is_scan_available {
151  my $self = shift;
152  return 0 unless $self->{conf}->{use_learner};
153  return $self->{main}->call_plugins("learner_is_scan_available");
154}
155
156###########################################################################
157
158sub dump_bayes_db {
159  my($self, $magic, $toks, $regex) = @_;
160  return 0 unless $self->{conf}->{use_learner};
161  return $self->{main}->call_plugins("learner_dump_database", {
162            magic => $magic, toks => $toks, regex => $regex });
163}
164
1651;
166