~ubuntu-branches/ubuntu/precise/spamassassin/precise-updates

« back to all changes in this revision

Viewing changes to lib/Mail/SpamAssassin/Plugin/Pyzor.pm

  • Committer: Bazaar Package Importer
  • Author(s): Noah Meyerhans
  • Date: 2010-01-26 22:53:12 UTC
  • mfrom: (1.1.13 upstream) (5.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20100126225312-wkftb10idc1kz2aq
Tags: 3.3.0-1
* New upstream version.
* Switch to dpkg-source 3.0 (quilt) format

Show diffs side-by-side

added added

removed removed

Lines of Context:
37
37
use Mail::SpamAssassin::Plugin;
38
38
use Mail::SpamAssassin::Logger;
39
39
use Mail::SpamAssassin::Timeout;
 
40
use Mail::SpamAssassin::Util qw(untaint_var untaint_file_path
 
41
                                proc_status_ok exit_status_str);
40
42
use strict;
41
43
use warnings;
42
44
use bytes;
 
45
use re 'taint';
43
46
 
44
47
use vars qw(@ISA);
45
48
@ISA = qw(Mail::SpamAssassin::Plugin);
71
74
 
72
75
sub set_config {
73
76
  my ($self, $conf) = @_;
74
 
  my @cmds = ();
 
77
  my @cmds;
75
78
 
76
79
=head1 USER OPTIONS
77
80
 
153
156
    setting => 'pyzor_options',
154
157
    is_admin => 1,
155
158
    default => '',
 
159
    type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
156
160
    code => sub {
157
161
      my ($self, $key, $value, $line) = @_;
158
162
      if ($value !~ m{^([0-9A-Za-z ,._/-]+)$}) {
175
179
    setting => 'pyzor_path',
176
180
    is_admin => 1,
177
181
    default => undef,
 
182
    type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
178
183
    code => sub {
179
184
      my ($self, $key, $value, $line) = @_;
180
185
      if (!defined $value || !length $value) {
181
186
        return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
182
187
      }
183
 
      $value = Mail::SpamAssassin::Util::untaint_file_path($value);
 
188
      $value = untaint_file_path($value);
184
189
      if (!-x $value) {
185
190
        info("config: pyzor_path \"$value\" isn't an executable");
186
191
        return $Mail::SpamAssassin::Conf::INVALID_VALUE;
236
241
  # initialize valid tags
237
242
  $permsgstatus->{tag_data}->{PYZOR} = "";
238
243
 
 
244
  my $timer = $self->{main}->time_method("check_pyzor");
 
245
 
239
246
  $self->get_pyzor_interface();
240
247
  return 0 unless $self->{pyzor_available};
241
248
 
257
264
  my $tmpf = $permsgstatus->create_fulltext_tmpfile($fulltext);
258
265
 
259
266
  # note: not really tainted, this came from system configuration file
260
 
  my $path = Mail::SpamAssassin::Util::untaint_file_path($self->{main}->{conf}->{pyzor_path});
261
 
 
262
 
  my $opts = $self->{main}->{conf}->{pyzor_options} || '';
 
267
  my $path = untaint_file_path($self->{main}->{conf}->{pyzor_path});
 
268
  my $opts = untaint_var($self->{main}->{conf}->{pyzor_options}) || '';
263
269
 
264
270
  $permsgstatus->enter_helper_run_mode();
265
271
 
266
 
  my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout });
 
272
  my $timer = Mail::SpamAssassin::Timeout->new(
 
273
           { secs => $timeout, deadline => $permsgstatus->{master_deadline} });
267
274
  my $err = $timer->run_and_catch(sub {
268
275
 
269
276
    local $SIG{PIPE} = sub { die "__brokenpipe__ignore__\n" };
274
281
        $tmpf, 1, $path, split(' ', $opts), "check");
275
282
    $pid or die "$!\n";
276
283
 
277
 
    @response = <PYZOR>;
278
 
    close PYZOR
279
 
      or dbg(sprintf("pyzor: [%s] finished: %s exit=0x%04x",$pid,$!,$?));
 
284
    # read+split avoids a Perl I/O bug (Bug 5985)
 
285
    my($inbuf,$nread,$resp); $resp = '';
 
286
    while ( $nread=read(PYZOR,$inbuf,8192) ) { $resp .= $inbuf }
 
287
    defined $nread  or die "error reading from pipe: $!";
 
288
    @response = split(/^/m, $resp, -1);  undef $resp;
 
289
 
 
290
    my $errno = 0;  close PYZOR or $errno = $!;
 
291
    if (proc_status_ok($?,$errno)) {
 
292
      dbg("pyzor: [%s] finished successfully", $pid);
 
293
    } elsif (proc_status_ok($?,$errno, 0,1)) {  # sometimes it exits with 1
 
294
      dbg("pyzor: [%s] finished: %s", $pid, exit_status_str($?,$errno));
 
295
    } else {
 
296
      info("pyzor: [%s] error: %s", $pid, exit_status_str($?,$errno));
 
297
    }
280
298
 
281
299
    if (!@response) {
282
300
      # this exact string is needed below
283
301
      die("no response\n");     # yes, this is possible
284
302
    }
285
 
    map { chomp } @response;
 
303
    chomp for @response;
286
304
    dbg("pyzor: got response: " . join("\\n", @response));
287
305
 
288
306
    if ($response[0] =~ /^Traceback/) {
289
 
      # this exact string is needed below
290
 
      die("internal error\n");
 
307
      die("internal error, python traceback seen in response\n");
291
308
    }
292
309
 
293
310
  });
297
314
      if (kill('TERM',$pid)) { dbg("pyzor: killed stale helper [$pid]") }
298
315
      else { dbg("pyzor: killing helper application [$pid] failed: $!") }
299
316
    }
300
 
    close PYZOR
301
 
      or dbg(sprintf("pyzor: [%s] terminated: %s exit=0x%04x",$pid,$!,$?));
 
317
    my $errno = 0;  close PYZOR or $errno = $!;
 
318
    proc_status_ok($?,$errno)
 
319
      or info("pyzor: [%s] error: %s", $pid, exit_status_str($?,$errno));
302
320
  }
303
321
  $permsgstatus->leave_helper_run_mode();
304
322
 
374
392
  my ($self, $options, $tmpf) = @_;
375
393
 
376
394
  # note: not really tainted, this came from system configuration file
377
 
  my $path = Mail::SpamAssassin::Util::untaint_file_path($options->{report}->{conf}->{pyzor_path});
 
395
  my $path = untaint_file_path($options->{report}->{conf}->{pyzor_path});
 
396
  my $opts = untaint_var($options->{report}->{conf}->{pyzor_options}) || '';
378
397
 
379
 
  my $opts = $options->{report}->{conf}->{pyzor_options} || '';
380
398
  my $timeout = $self->{main}->{conf}->{pyzor_timeout};
381
399
 
382
400
  $options->{report}->enter_helper_run_mode();
392
410
        $tmpf, 1, $path, split(' ', $opts), "report");
393
411
    $pid or die "$!\n";
394
412
 
395
 
    my @ignored = <PYZOR>;
396
 
    $options->{report}->close_pipe_fh(\*PYZOR);
397
 
 
398
 
    waitpid ($pid, 0);
 
413
    my($inbuf,$nread,$nread_all); $nread_all = 0;
 
414
    # response is ignored, just check its existence
 
415
    while ( $nread=read(PYZOR,$inbuf,8192) ) { $nread_all += $nread }
 
416
    defined $nread  or die "error reading from pipe: $!";
 
417
 
 
418
    dbg("pyzor: empty response")  if $nread_all < 1;
 
419
 
 
420
    my $errno = 0;  close PYZOR or $errno = $!;
 
421
    # closing a pipe also waits for the process executing on the pipe to
 
422
    # complete, no need to explicitly call waitpid
 
423
    # my $child_stat = waitpid($pid,0) > 0 ? $? : undef;
 
424
    if (proc_status_ok($?,$errno, 0)) {
 
425
      dbg("pyzor: [%s] reporter finished successfully", $pid);
 
426
    } else {
 
427
      info("pyzor: [%s] reporter error: %s", $pid, exit_status_str($?,$errno));
 
428
    }
 
429
 
399
430
  });
400
431
 
401
432
  $options->{report}->leave_helper_run_mode();