PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Mail/Decency/Detective/Core/Cmd.pm

https://github.com/ukautz/decency
Perl | 253 lines | 137 code | 91 blank | 25 comment | 15 complexity | 0cc7e9eb284849856aec143fb8d1d9f3 MD5 | raw file
  1. package Mail::Decency::Detective::Core::Cmd;
  2. use Mouse::Role;
  3. #extends 'Mail::Decency::Detective::Core';
  4. #with qw/ Mail::Decency::Detective::Core::User /;
  5. use version 0.74; our $VERSION = qv( "v0.2.0" );
  6. use Data::Dumper;
  7. use IO::Pipe;
  8. =head1 NAME
  9. Mail::Decency::Detective::Core::Cmd
  10. =head1 DESCRIPTION
  11. Base class for all command line filters. Including spam filter such as DSPAM and so on
  12. =head1 CLASS ATTRIBUTES
  13. =head2 cmd_check : Str
  14. Check command.. normally the command which is used to filter a single mail.
  15. =cut
  16. has cmd_check => ( is => 'rw', isa => 'Str' );
  17. =head2 cmd_learn_ham : Str
  18. Learn HAM command..
  19. =cut
  20. has cmd_learn_ham => ( is => 'rw', isa => 'Str', predicate => 'can_learn_ham' );
  21. =head2 cmd_learn_ham : Str
  22. UNLearn HAM command.. (wrongly trained before)
  23. =cut
  24. has cmd_unlearn_ham => ( is => 'rw', isa => 'Str', predicate => 'can_unlearn_ham' );
  25. =head2 cmd_learn_spam : Str
  26. Learn new SPAM
  27. =cut
  28. has cmd_learn_spam => ( is => 'rw', isa => 'Str', predicate => 'can_learn_spam' );
  29. =head2 cmd_unlearn_spam : Str
  30. Unlearn wrongly trained SPAM
  31. =cut
  32. has cmd_unlearn_spam => ( is => 'rw', isa => 'Str', predicate => 'can_learn_spam' );
  33. =head1 REQUIRED METHODS
  34. =head2 handle_filter_result
  35. Will be called
  36. $self->handle_filter_result( $cmd_filter_output, $exit_code );
  37. =cut
  38. requires qw/ handle_filter_result /;
  39. =head1 METHOD MODIFIERS
  40. =head2 before pre_init
  41. Add check params: cmd, check, train and untrain to list of check params
  42. =cut
  43. before pre_init => sub {
  44. shift->add_config_params( qw/
  45. cmd_check
  46. cmd_learn_ham
  47. cmd_unlearn_ham
  48. cmd_learn_spam
  49. cmd_unlearn_spam
  50. / );
  51. };
  52. =head1 METHODS
  53. =head2 handle
  54. Handle command-line calls
  55. =cut
  56. sub handle {
  57. my ( $self ) = @_;
  58. # pipe file throught command
  59. my ( $res, $result, $exit_code ) = $self->cmd_filter;
  60. # return if cannot be handled
  61. return unless $res;
  62. # chomp lines
  63. 1 while chomp $result;
  64. # handle result by the actual filter module
  65. return $self->handle_filter_result( $result, $exit_code );
  66. }
  67. =head2 cmd_filter
  68. Pipes mail content through command line program and caches result
  69. =cut
  70. sub cmd_filter {
  71. my ( $self, $cmd_type ) = @_;
  72. $cmd_type ||= 'check';
  73. # retreive user
  74. my $user = $self->get_user();
  75. # build command
  76. my $cmd = $self->build_cmd( $cmd_type );
  77. # if command required user and no user could be determined -> abort
  78. if ( $cmd =~ /%user%/ && ! $user ) {
  79. $self->logger->error( "Could not determine user for recipient ". $self->to. ", command line '$cmd'. ABORT!" );
  80. return ( 0 );
  81. }
  82. # replace user in command
  83. $cmd =~ s/%user%/$user/g if $user;
  84. $self->logger->debug3( "Run cmd '$cmd'" );
  85. # we cannot redirect STDOUT and STDERR in a multi-process environment!
  86. # instead we'll use a tem file
  87. my ( $th, $tn ) = $self->get_temp_file( $self->server->temp_dir, "file-XXXXXX" );
  88. # wheter uses stdin or use file name
  89. my $stdin = 1;
  90. my ( $input_handle, $input_file ); # in handle, in file
  91. # pritn to file and give this file to command line
  92. my $file_mode = 0;
  93. if ( $cmd =~ /%file%/ ) {
  94. $file_mode++;
  95. ( $input_handle, $input_file )
  96. = $self->get_temp_file( $self->server->temp_dir, "file-XXXXXX" );
  97. # ( $input_handle, $input_file )
  98. # = tempfile( $self->server->temp_dir. "/file-XXXXXX", UNLINK => 0 );
  99. }
  100. # open command line and print to it
  101. else {
  102. {
  103. # yeah, zombie mess..
  104. local $SIG{ CHLD } = 'IGNORE';
  105. open $input_handle, '|-', "$cmd 1>\"$tn\" 2>\"$tn\"";
  106. };
  107. $self->add_file_handle( $input_handle );
  108. }
  109. # open now the mail file and pipe
  110. my $fh = $self->open_file( '<', $self->file );
  111. # print whole mime data to pipe
  112. while ( my $l = <$fh> ) {
  113. print $input_handle $l;
  114. }
  115. # close input and command
  116. close $fh;
  117. close $input_handle;
  118. # in file mode: provide file name as input
  119. my $system_result = 0;
  120. if ( $file_mode ) {
  121. ( my $cmd_file = $cmd ) =~ s/%file%/$input_file/;
  122. $self->logger->debug3( "Run command '$cmd_file'" );
  123. {
  124. # zombies are creepy
  125. local $SIG{ CHLD } = 'IGNORE';
  126. `$cmd_file 1>"$tn" 2>"$tn"`;
  127. $system_result = $?;
  128. };
  129. }
  130. # read now output from tempfile and remove it.. break after first empty line
  131. # to assure we'll get only headers!!
  132. reset $th;
  133. my $in = "";
  134. while ( my $l = <$th> ) {
  135. last if $l =~ /^$/;
  136. $in .= $l;
  137. }
  138. close $th;
  139. unlink( $tn ) if -f $tn;
  140. unlink( $input_file ) if $input_file && -f $input_file;
  141. return ( 1, $in, $system_result );
  142. }
  143. =head2 build_cmd
  144. Can be overwritte by descendant module
  145. Build cmd
  146. =cut
  147. sub build_cmd {
  148. my ( $self, $type ) = @_;
  149. my $meth = "cmd_$type";
  150. return $self->$meth;
  151. }
  152. sub _exec_cmd {
  153. my ( $system )
  154. }
  155. =head1 AUTHOR
  156. Ulrich Kautz <uk@fortrabbit.de>
  157. =head1 COPYRIGHT
  158. Copyright (c) 2011 the L</AUTHOR> as listed above
  159. =head1 LICENCSE
  160. This library is free software and may be distributed under the same terms as perl itself.
  161. =cut
  162. 1;