21
20
Mail::SpamAssassin::Client - Client for spamd Protocol
23
NOTE: This interface is alpha at best, and almost guaranteed to change
27
my $client = new Mail::SpamAssassin::Client({port => 783,
29
username => 'someuser'});
24
my $client = new Mail::SpamAssassin::Client({
27
username => 'someuser'});
30
my $client = new Mail::SpamAssassin::Client({
31
socketpath => '/path/to/socket',
32
username => 'someuser'});
34
Optionally takes timeout, which is applied to IO::Socket for the
35
initial connection. If not supplied, it defaults to 30 seconds.
31
37
if ($client->ping()) {
32
38
print "Ping is ok\n";
43
Mail::SpamAssassin::Client is a module that provides a perl implementation for
49
Mail::SpamAssassin::Client is a module which provides a perl implementation of
44
50
the spamd protocol.
48
54
package Mail::SpamAssassin::Client;
52
63
my $EOL = "\015\012";
53
64
my $BLANK = $EOL x 2;
54
my $PROTOVERSION = 'SPAMC/1.3';
65
my $PROTOVERSION = 'SPAMC/1.5';
56
67
=head1 PUBLIC METHODS
94
public instance (\%) process (String $msg, Boolean $is_check_p)
109
public instance (\%) process (String $msg)
97
This method makes a call to the spamd server and depending on the value of
98
C<$is_check_p> either calls PROCESS or CHECK.
112
This method calls the spamd server with the PROCESS command.
100
114
The return value is a hash reference containing several pieces of information,
116
130
my ($self, $msg, $is_check_p) = @_;
120
my $command = $is_check_p ? 'CHECK' : 'PROCESS';
122
$self->_clear_errors();
124
my $remote = $self->_create_connection();
126
return 0 unless ($remote);
128
my $msgsize = length($msg.$EOL);
130
print $remote "$command $PROTOVERSION$EOL";
131
print $remote "Content-length: $msgsize$EOL";
132
print $remote "User: $self->{username}$EOL" if ($self->{username});
133
print $remote "$EOL";
135
print $remote "$EOL";
137
my $line = <$remote>;
138
return undef unless (defined $line);
140
my ($version, $resp_code, $resp_msg) = $self->_parse_response_line($line);
142
$self->{resp_code} = $resp_code;
143
$self->{resp_msg} = $resp_msg;
145
return undef unless ($resp_code == 0);
147
while ($line = <$remote>) {
148
if ($line =~ /Content-length: (\d+)/) {
149
$data{content_length} = $1;
151
elsif ($line =~ m!Spam: (\S+) ; (\S+) / (\S+)!) {
153
$data{score} = $2 + 0;
154
$data{threshold} = $3 + 0;
156
elsif ($line =~ /^${EOL}$/) {
166
$data{message} = $return_msg if ($return_msg);
132
my $command = 'PROCESS';
135
warn "Passing in \$is_check_p is deprecated, just call the check method instead.\n";
139
return $self->_filter($msg, $command);
178
147
The method implements the check call.
180
Since check and process are so similar, we simply pass this
181
call along to the process method with a flag to indicate
182
to actually make the CHECK call.
184
149
See the process method for the return value.
189
154
my ($self, $msg) = @_;
191
return $self->process($msg, 1);
156
return $self->_filter($msg, 'CHECK');
161
public instance (\%) headers (String $msg)
164
This method implements the headers call.
166
See the process method for the return value.
171
my ($self, $msg) = @_;
173
return $self->_filter($msg, 'HEADERS');
220
202
print $remote "TELL $PROTOVERSION$EOL";
221
203
print $remote "Content-length: $msgsize$EOL";
222
print $remote "User: $self->{username}$EOL" if ($self->{username});
204
print $remote "User: $self->{username}$EOL" if defined $self->{username};
224
206
if ($learntype == 0) {
225
207
print $remote "Message-class: spam$EOL";
242
224
print $remote $msg;
243
225
print $remote "$EOL";
245
my $line = <$remote>;
227
$! = 0; my $line = <$remote>;
228
# deal gracefully with a Perl I/O bug which may return status EBADF at eof
229
defined $line || $!==0 or
230
$!==EBADF ? dbg("error reading from spamd (1): $!")
231
: die "error reading from spamd (1): $!";
246
232
return undef unless (defined $line);
248
234
my ($version, $resp_code, $resp_msg) = $self->_parse_response_line($line);
256
defined $line || $!==0 or
257
$!==EBADF ? dbg("error reading from spamd (2): $!")
258
: die "error reading from spamd (2): $!";
259
close $remote or die "error closing socket: $!";
272
261
if ($learntype == 0 || $learntype == 1) {
273
262
return $did_set =~ /local/;
300
289
print $remote "TELL $PROTOVERSION$EOL";
301
290
print $remote "Content-length: $msgsize$EOL";
302
print $remote "User: $self->{username}$EOL" if ($self->{username});
291
print $remote "User: $self->{username}$EOL" if defined $self->{username};
303
292
print $remote "Message-class: spam$EOL";
304
293
print $remote "Set: local,remote$EOL";
305
294
print $remote "$EOL";
306
295
print $remote $msg;
307
296
print $remote "$EOL";
309
my $line = <$remote>;
298
$! = 0; my $line = <$remote>;
299
defined $line || $!==0 or
300
$!==EBADF ? dbg("error reading from spamd (3): $!")
301
: die "error reading from spamd (3): $!";
310
302
return undef unless (defined $line);
312
304
my ($version, $resp_code, $resp_msg) = $self->_parse_response_line($line);
356
350
print $remote "TELL $PROTOVERSION$EOL";
357
351
print $remote "Content-length: $msgsize$EOL";
358
print $remote "User: $self->{username}$EOL" if ($self->{username});
352
print $remote "User: $self->{username}$EOL" if defined $self->{username};
359
353
print $remote "Message-class: ham$EOL";
360
354
print $remote "Set: local$EOL";
361
355
print $remote "Remove: remote$EOL";
363
357
print $remote $msg;
364
358
print $remote "$EOL";
366
my $line = <$remote>;
360
$! = 0; my $line = <$remote>;
361
defined $line || $!==0 or
362
$!==EBADF ? dbg("error reading from spamd (5): $!")
363
: die "error reading from spamd (5): $!";
367
364
return undef unless (defined $line);
369
366
my ($version, $resp_code, $resp_msg) = $self->_parse_response_line($line);
409
408
return 0 unless ($remote);
411
410
print $remote "PING $PROTOVERSION$EOL";
412
print $remote "$EOL";
411
print $remote "$EOL"; # bug 6187, bumps protocol version to 1.5
414
my $line = <$remote>;
413
$! = 0; my $line = <$remote>;
414
defined $line || $!==0 or
415
$!==EBADF ? dbg("error reading from spamd (7): $!")
416
: die "error reading from spamd (7): $!";
417
close $remote or die "error closing socket: $!";
416
418
return undef unless (defined $line);
418
420
my ($version, $resp_code, $resp_msg) = $self->_parse_response_line($line);
443
445
if ($self->{socketpath}) {
444
446
$remote = IO::Socket::UNIX->new( Peer => $self->{socketpath},
445
447
Type => SOCK_STREAM,
448
Timeout => $self->{timeout},
449
452
$remote = IO::Socket::INET->new( Proto => "tcp",
450
453
PeerAddr => $self->{host},
451
454
PeerPort => $self->{port},
455
Timeout => $self->{timeout},
496
500
$self->{resp_msg} = undef;
505
private instance (\%) _filter (String $msg, String $command)
508
Makes the actual call to the spamd server for the various filter method
509
(ie PROCESS, CHECK, HEADERS, etc). The command that is passed in is
510
sent to the spamd server.
512
The return value is a hash reference containing several pieces of information,
523
message (if available)
528
my ($self, $msg, $command) = @_;
532
$self->_clear_errors();
534
my $remote = $self->_create_connection();
536
return 0 unless ($remote);
538
my $msgsize = length($msg.$EOL);
540
print $remote "$command $PROTOVERSION$EOL";
541
print $remote "Content-length: $msgsize$EOL";
542
print $remote "User: $self->{username}$EOL" if defined $self->{username};
543
print $remote "$EOL";
545
print $remote "$EOL";
547
$! = 0; my $line = <$remote>;
548
defined $line || $!==0 or
549
$!==EBADF ? dbg("error reading from spamd (8): $!")
550
: die "error reading from spamd (8): $!";
551
return undef unless (defined $line);
553
my ($version, $resp_code, $resp_msg) = $self->_parse_response_line($line);
555
$self->{resp_code} = $resp_code;
556
$self->{resp_msg} = $resp_msg;
558
return undef unless ($resp_code == 0);
560
for ($!=0; defined($line=<$remote>); $!=0) {
562
if ($line =~ /Content-length: (\d+)/) {
563
$data{content_length} = $1;
565
elsif ($line =~ m!Spam: (\S+) ; (\S+) / (\S+)!) {
567
$data{score} = $2 + 0;
568
$data{threshold} = $3 + 0;
570
elsif ($line =~ /^${EOL}$/) {
574
defined $line || $!==0 or
575
$!==EBADF ? dbg("error reading from spamd (9): $!")
576
: die "error reading from spamd (9): $!";
579
for ($!=0; defined($line=<$remote>); $!=0) {
580
$return_msg .= $line;
582
defined $line || $!==0 or
583
$!==EBADF ? dbg("error reading from spamd (10): $!")
584
: die "error reading from spamd (10): $!";
586
$data{message} = $return_msg if ($return_msg);
588
close $remote or die "error closing socket: $!";