home *** CD-ROM | disk | FTP | other *** search
/ ftp.f-secure.com / 2014.06.ftp.f-secure.com.tar / ftp.f-secure.com / support / hotfix / fsis / IS-SpamControl.fsfix / iufssc / lib / Mail / SpamAssassin / PluginHandler.pm < prev    next >
Text File  |  2006-11-29  |  6KB  |  197 lines

  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. #     http://www.apache.org/licenses/LICENSE-2.0
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # </@LICENSE>
  15.  
  16. =head1 NAME
  17.  
  18. Mail::SpamAssassin::PluginHandler - SpamAssassin plugin handler
  19.  
  20. =cut
  21.  
  22. package Mail::SpamAssassin::PluginHandler;
  23.  
  24. use Mail::SpamAssassin;
  25. use Mail::SpamAssassin::Plugin;
  26. use Mail::SpamAssassin::Util;
  27. use Mail::SpamAssassin::Logger;
  28.  
  29. use strict;
  30. use warnings;
  31. use bytes;
  32. use File::Spec;
  33.  
  34. use vars qw{
  35.   @ISA $VERSION @CONFIG_TIME_HOOKS
  36. };
  37.  
  38. @ISA = qw();
  39.  
  40. $VERSION = 'bogus';     # avoid CPAN.pm picking up version strings later
  41.  
  42. # Normally, the list of active plugins that should be called for a given hook
  43. # method name is compiled and cached at runtime.  This means that later calls
  44. # will not have to traverse the entire plugin list more than once, since the
  45. # list of plugins that implement that hook is already cached.
  46. #
  47. # However, some hooks should not receive this treatment. One of these is
  48. # parse_config, which may be compiled before all config files have been read;
  49. # if a plugin is loaded from a config file after this has been compiled, it
  50. # will not get callbacks.
  51. #
  52. # Any other such hooks that may be compiled at config-parse-time should be
  53. # listed here.
  54.  
  55. @CONFIG_TIME_HOOKS = qw( parse_config );
  56.  
  57. ###########################################################################
  58.  
  59. sub new {
  60.   my $class = shift;
  61.   my $main = shift;
  62.   $class = ref($class) || $class;
  63.   my $self = {
  64.     plugins        => [ ],
  65.     cbs         => { },
  66.     main        => $main
  67.   };
  68.   bless ($self, $class);
  69.   $self;
  70. }
  71.  
  72. ###########################################################################
  73.  
  74. sub load_plugin {
  75.   my ($self, $package, $path) = @_;
  76.  
  77.   my $ret;
  78.   if ($path) {
  79.     # bug 3717:
  80.     # At least Perl 5.8.0 seems to confuse $cwd internally at some point -- we
  81.     # need to use an absolute path here else we get a "File not found" error.
  82.     $path = Mail::SpamAssassin::Util::untaint_file_path(
  83.               File::Spec->rel2abs($path)
  84.         );
  85.     dbg("plugin: loading $package from $path");
  86.     $ret = do $path;
  87.   }
  88.   else {
  89.     dbg("plugin: loading $package from \@INC");
  90.     $ret = eval qq{ require $package; };
  91.     $path = "(from \@INC)";
  92.   }
  93.  
  94.   if (!$ret) {
  95.     if ($@) { warn "plugin: failed to parse plugin $path: $@\n"; }
  96.     elsif ($!) { warn "plugin: failed to load plugin $path: $!\n"; }
  97.   }
  98.  
  99.   my $plugin = eval $package.q{->new ($self->{main}); };
  100.  
  101.   if ($@ || !$plugin) { warn "plugin: failed to create instance of plugin $package: $@\n"; }
  102.  
  103.   # Don't load the same plugin twice!
  104.   foreach my $old_plugin (@{$self->{plugins}}) {
  105.     if (ref($old_plugin) eq ref($plugin)) {
  106.       dbg("plugin: did not register $plugin, already registered");
  107.       return;
  108.     }
  109.   }
  110.  
  111.   if ($plugin) {
  112.     $self->{main}->{plugins}->register_plugin ($plugin);
  113.     $self->{main}->{conf}->load_plugin_succeeded ($plugin, $package, $path);
  114.   }
  115. }
  116.  
  117. sub register_plugin {
  118.   my ($self, $plugin) = @_;
  119.   $plugin->{main} = $self->{main};
  120.   push (@{$self->{plugins}}, $plugin);
  121.   dbg("plugin: registered $plugin");
  122.  
  123.   # invalidate cache entries for any configuration-time hooks, in case
  124.   # one has already been built; this plugin may implement that hook!
  125.   foreach my $subname (@CONFIG_TIME_HOOKS) {
  126.     delete $self->{cbs}->{$subname};
  127.   }
  128. }
  129.  
  130. ###########################################################################
  131.  
  132. sub callback {
  133.   my $self = shift;
  134.   my $subname = shift;
  135.   my ($ret, $overallret);
  136.  
  137.   # have we set up the cache entry for this callback type?
  138.   if (!exists $self->{cbs}->{$subname}) {
  139.     # nope.  run through all registered plugins and see which ones
  140.     # implement this type of callback
  141.     my @subs = ();
  142.     foreach my $plugin (@{$self->{plugins}}) {
  143.       my $methodref = $plugin->can ($subname);
  144.       if (defined $methodref) {
  145.         push (@subs, [ $plugin, $methodref ]);
  146.         dbg("plugin: ${plugin} implements '$subname'");
  147.       }
  148.     }
  149.     $self->{cbs}->{$subname} = \@subs;
  150.   }
  151.  
  152.   foreach my $cbpair (@{$self->{cbs}->{$subname}}) {
  153.     my ($plugin, $methodref) = @$cbpair;
  154.  
  155.     $plugin->{_inhibit_further_callbacks} = 0;
  156.  
  157.     eval {
  158.       $ret = &$methodref ($plugin, @_);
  159.     };
  160.     if ($@) {
  161.       warn "plugin: eval failed: $@";
  162.     }
  163.  
  164.     if ($ret) {
  165.       #dbg("plugin: ${plugin}->${methodref} => $ret");
  166.       $overallret = $ret;
  167.     }
  168.  
  169.     if ($plugin->{_inhibit_further_callbacks}) {
  170.       # dbg("plugin: $plugin inhibited further callbacks");
  171.       last;
  172.     }
  173.   }
  174.  
  175.   $overallret ||= $ret;
  176.   return $overallret;
  177. }
  178.  
  179. ###########################################################################
  180.  
  181. sub finish {
  182.   my $self = shift;
  183.   delete $self->{cbs};
  184.   foreach my $plugin (@{$self->{plugins}}) {
  185.     $plugin->finish();
  186.     delete $plugin->{main};
  187.   }
  188.   delete $self->{plugins};
  189.   delete $self->{main};
  190. }
  191.  
  192. ###########################################################################
  193.  
  194. 1;
  195.