Server IP : 85.214.239.14 / Your IP : 3.145.175.195 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /usr/share/perl5/Mail/SpamAssassin/ |
Upload File : |
# <@LICENSE> # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to you under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # </@LICENSE> =head1 NAME Mail::SpamAssassin::PluginHandler - SpamAssassin plugin handler =cut package Mail::SpamAssassin::PluginHandler; use Mail::SpamAssassin; use Mail::SpamAssassin::Plugin; use Mail::SpamAssassin::Util; use Mail::SpamAssassin::Logger; use strict; use warnings; # use bytes; use re 'taint'; use File::Spec; our @ISA = qw(); #Removed $VERSION per BUG 6422 #$VERSION = 'bogus'; # avoid CPAN.pm picking up version strings later # Normally, the list of active plugins that should be called for a given hook # method name is compiled and cached at runtime. This means that later calls # will not have to traverse the entire plugin list more than once, since the # list of plugins that implement that hook is already cached. # # However, some hooks should not receive this treatment. One of these is # parse_config, which may be compiled before all config files have been read; # if a plugin is loaded from a config file after this has been compiled, it # will not get callbacks. # # Any other such hooks that may be compiled at config-parse-time should be # listed here. our @CONFIG_TIME_HOOKS = qw( parse_config ); ########################################################################### sub new { my $class = shift; my $main = shift; $class = ref($class) || $class; my $self = { plugins => [ ], cbs => { }, main => $main }; bless ($self, $class); $self; } ########################################################################### sub load_plugin { my ($self, $package, $path, $silent) = @_; # Strict name checking if ($package !~ /^(?:\w+::){0,10}\w+$/) { warn "plugin: illegal plugin name, not loading: $package\n"; return; } $package = Mail::SpamAssassin::Util::untaint_var($package); # Bug 7728 if ($package eq 'Mail::SpamAssassin::Plugin::HashCash') { warn "plugin: $package is deprecated, remove loadplugin clause from your configuration\n"; return; } # Don't load the same plugin twice! # Do this *before* calling ->new(), otherwise eval rules will be # registered on a nonexistent object foreach my $old_plugin (@{$self->{plugins}}) { if (ref($old_plugin) eq $package) { dbg("plugin: did not register $package, already registered"); return; } } my $ret; if ($path) { if ($path !~ /^\S+\.pm/i) { warn "plugin: illegal plugin filename, not loading: $path"; return; } $path = $self->{main}->{conf}->{parser}->fix_path_relative_to_current_file($path); # bug 3717: # At least Perl 5.8.0 seems to confuse $cwd internally at some point -- we # need to use an absolute path here else we get a "File not found" error. $path = Mail::SpamAssassin::Util::untaint_file_path( File::Spec->rel2abs($path) ); # if (exists $INC{$path}) { # dbg("plugin: not loading $package from $path, already loaded"); # return; # } dbg("plugin: loading $package from $path"); # use require instead of "do", so we get built-in $INC{filename} # smarts $ret = eval { require $path; }; } else { dbg("plugin: loading $package from \@INC"); $ret = eval qq{ require $package; }; $path = "(from \@INC)"; } if (!$ret) { if ($silent) { if ($@) { dbg("plugin: failed to parse tryplugin $path: $@\n"); } elsif ($!) { dbg("plugin: failed to load tryplugin $path: $!\n"); } } else { if ($@) { warn "plugin: failed to parse plugin $path: $@\n"; } elsif ($!) { warn "plugin: failed to load plugin $path: $!\n"; } } return; # failure! no point in continuing here } my $plugin = eval $package.q{->new ($self->{main}); }; if ($@ || !$plugin) { warn "plugin: failed to create instance of plugin $package: $@\n"; } if ($plugin) { $self->{main}->{plugins}->register_plugin ($plugin); $self->{main}->{conf}->load_plugin_succeeded ($plugin, $package, $path); } } sub register_plugin { my ($self, $plugin) = @_; $plugin->{main} = $self->{main}; push (@{$self->{plugins}}, $plugin); # dbg("plugin: registered $plugin"); # invalidate cache entries for any configuration-time hooks, in case # one has already been built; this plugin may implement that hook! foreach my $subname (@CONFIG_TIME_HOOKS) { delete $self->{cbs}->{$subname}; } } ########################################################################### sub have_callback { my ($self, $subname) = @_; # have we set up the cache entry for this callback type? if (!exists $self->{cbs}->{$subname}) { # nope. run through all registered plugins and see which ones # implement this type of callback. sort by priority my %subsbypri; foreach my $plugin (@{$self->{plugins}}) { my $methodref = $plugin->can ($subname); if (defined $methodref) { my $pri = $plugin->{method_priority}->{$subname} || 0; $subsbypri{$pri} ||= []; push (@{$subsbypri{$pri}}, [ $plugin, $methodref ]); dbg("plugin: ${plugin} implements '$subname', priority $pri"); } } my @subs; foreach my $pri (sort { $a <=> $b } keys %subsbypri) { push @subs, @{$subsbypri{$pri}}; } $self->{cbs}->{$subname} = \@subs; } return scalar(@{$self->{cbs}->{$subname}}); } sub callback { my $self = shift; my $subname = shift; my ($ret, $overallret); # have we set up the cache entry for this callback type? if (!exists $self->{cbs}->{$subname}) { return unless $self->have_callback($subname); } foreach my $cbpair (@{$self->{cbs}->{$subname}}) { my ($plugin, $methodref) = @$cbpair; $plugin->{_inhibit_further_callbacks} = 0; eval { $ret = &$methodref ($plugin, @_); 1; } or do { my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; warn "plugin: eval failed: $eval_stat\n"; }; if (defined $ret) { # dbg("plugin: ${plugin}->${methodref} => $ret"); # we are interested in defined but false results too $overallret = $ret if $ret || !defined $overallret; } if ($plugin->{_inhibit_further_callbacks}) { # dbg("plugin: $plugin inhibited further callbacks"); last; } } return $overallret; } ########################################################################### sub get_loaded_plugins_list { my ($self) = @_; return @{$self->{plugins}}; } ########################################################################### sub finish { my $self = shift; delete $self->{cbs}; foreach my $plugin (@{$self->{plugins}}) { $plugin->finish(); delete $plugin->{main}; } delete $self->{plugins}; delete $self->{main}; } ########################################################################### 1;