home *** CD-ROM | disk | FTP | other *** search
/ PC Open 101 / PC Open 101 CD 1.bin / CD1 / INTERNET / EMAIL / pop file / setup.exe / POPFile / Configuration.pm < prev    next >
Encoding:
Perl POD Document  |  2004-08-02  |  20.4 KB  |  690 lines

  1. # POPFILE LOADABLE MODULE
  2. package POPFile::Configuration;
  3.  
  4. use POPFile::Module;
  5. @ISA = ( "POPFile::Module" );
  6.  
  7. #----------------------------------------------------------------------------
  8. #
  9. # This module handles POPFile's configuration parameters.  It is used to
  10. # load and save from the popfile.cfg file and individual POPFile modules
  11. # register specific parameters with this module.  This module also handles
  12. # POPFile's command line parsing
  13. #
  14. # Copyright (c) 2001-2004 John Graham-Cumming
  15. #
  16. #   This file is part of POPFile
  17. #
  18. #   POPFile is free software; you can redistribute it and/or modify
  19. #   it under the terms of the GNU General Public License as published by
  20. #   the Free Software Foundation; either version 2 of the License, or
  21. #   (at your option) any later version.
  22. #
  23. #   POPFile is distributed in the hope that it will be useful,
  24. #   but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  26. #   GNU General Public License for more details.
  27. #
  28. #   You should have received a copy of the GNU General Public License
  29. #   along with POPFile; if not, write to the Free Software
  30. #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  31. #
  32. #----------------------------------------------------------------------------
  33.  
  34. use strict;
  35. use warnings;
  36. use locale;
  37.  
  38. use Getopt::Long;
  39.  
  40. #----------------------------------------------------------------------------
  41. # new
  42. #
  43. #   Class new() function
  44. #----------------------------------------------------------------------------
  45. sub new
  46. {
  47.     my $type = shift;
  48.     my $self = POPFile::Module->new();
  49.  
  50.     # This hash has indexed by parameter name and has two fields:
  51.     #
  52.     # value         The current value
  53.     # default       The default value
  54.  
  55.     $self->{configuration_parameters__} = {};
  56.  
  57.     # Name of the PID file that we created
  58.  
  59.     $self->{pid_file__} = '';
  60.  
  61.     # The time to delay checking of the PID file
  62.  
  63.     $self->{pid_delay__} = 5;
  64.  
  65.     # The last time the PID was checked
  66.  
  67.     $self->{pid_check__} = time;
  68.  
  69.     # Used to tell whether we need to save the configuration
  70.  
  71.     $self->{save_needed__} = 0;
  72.  
  73.     # We track when out start() is called so that we know when the modules
  74.     # are done setting the default values so that we know which have default
  75.     # and which do not
  76.  
  77.     $self->{started__} = 0;
  78.  
  79.     # Local copies of POPFILE_ROOT and POPFILE_USER
  80.  
  81.     $self->{popfile_root__} = $ENV{POPFILE_ROOT} || './';
  82.     $self->{popfile_user__} = $ENV{POPFILE_USER} || './';
  83.  
  84.     bless $self, $type;
  85.  
  86.     $self->name( 'config' );
  87.  
  88.     return $self;
  89. }
  90.  
  91. # ---------------------------------------------------------------------------------------------
  92. #
  93. # initialize
  94. #
  95. # Called to initialize the interface
  96. #
  97. # ---------------------------------------------------------------------------------------------
  98. sub initialize
  99. {
  100.     my ( $self ) = @_;
  101.  
  102.     # This is the location where we store the PID of POPFile in a file
  103.     # called popfile.pid
  104.  
  105.     $self->config_( 'piddir', './' );
  106.  
  107.     # The default timeout in seconds for POP3 commands
  108.  
  109.     $self->global_config_( 'timeout', 60 );
  110.  
  111.     # The default location for the message files
  112.  
  113.     $self->global_config_( 'msgdir', 'messages/' );
  114.  
  115.     # The maximum number of characters to consider in a message during
  116.     # classification, display or reclassification
  117.  
  118.     $self->global_config_( 'message_cutoff', 100000 );
  119.  
  120.     # Register for the TICKD message which is sent hourly by the
  121.     # Logger module.   We use this to hourly save the configuration file
  122.     # so that POPFile's configuration is saved in case of a hard crash.
  123.     #
  124.     # This is particularly needed by the IMAP module which stores some
  125.     # state related information in the configuration parameters.  Note that
  126.     # because of the save_needed__ bool there wont be any write to the
  127.     # disk unless a configuration parameter has been changed since the
  128.     # last save.  (see parameter())
  129.  
  130.     $self->mq_register_( 'TICKD', $self );
  131.  
  132.     return 1;
  133. }
  134.  
  135. # ---------------------------------------------------------------------------------------------
  136. #
  137. # start
  138. #
  139. # Called to start this module
  140. #
  141. # ---------------------------------------------------------------------------------------------
  142. sub start
  143. {
  144.     my ( $self ) = @_;
  145.  
  146.     $self->{started__} = 1;
  147.  
  148.     # Check to see if the PID file is present, if it is then another POPFile
  149.     # may be running, warn the user and terminate, note the 0 at the end
  150.     # means that we allow the piddir to be absolute and outside the user
  151.     # sandbox
  152.  
  153.     $self->{pid_file__} = $self->get_user_path( $self->config_( 'piddir' ) . 'popfile.pid', 0 );
  154.  
  155.     if (defined($self->live_check_())) {
  156.         return 0;
  157.     }
  158.  
  159.     $self->write_pid_();
  160.  
  161.     return 1;
  162. }
  163.  
  164. # ---------------------------------------------------------------------------------------------
  165. #
  166. # service
  167. #
  168. # service() is a called periodically to give the module a chance to do housekeeping work.
  169. #
  170. # If any problem occurs that requires POPFile to shutdown service() should return 0 and
  171. # the top level process will gracefully terminate POPFile including calling all stop()
  172. # methods.  In normal operation return 1.#
  173. # ---------------------------------------------------------------------------------------------
  174. sub service
  175. {
  176.     my ( $self ) = @_;
  177.  
  178.     my $time = time;
  179.  
  180.     if ( $self->{pid_check__} <= ( $time - $self->{pid_delay__})) {
  181.  
  182.         $self->{pid_check__} = $time;
  183.  
  184.         if ( !$self->check_pid_() ) {
  185.             $self->write_pid_();
  186.             $self->log_( 0, "New POPFile instance detected and signalled" );
  187.         }
  188.     }
  189.  
  190.     return 1;
  191. }
  192.  
  193. # ---------------------------------------------------------------------------------------------
  194. #
  195. # stop
  196. #
  197. # Called to shutdown this module
  198. #
  199. # ---------------------------------------------------------------------------------------------
  200. sub stop
  201. {
  202.     my ( $self ) = @_;
  203.  
  204.     $self->save_configuration();
  205.  
  206.     $self->delete_pid_();
  207. }
  208.  
  209. # ---------------------------------------------------------------------------------------------
  210. #
  211. # deliver
  212. #
  213. # Called by the message queue to deliver a message
  214. #
  215. # ---------------------------------------------------------------------------------------------
  216. sub deliver
  217. {
  218.     my ( $self, $type, @message ) = @_;
  219.  
  220.     if ( $type eq 'TICKD' ) {
  221.         $self->save_configuration();
  222.     }
  223. }
  224.  
  225. # ---------------------------------------------------------------------------------------------
  226. #
  227. # live_check_
  228. #
  229. # Checks if an instance of POPFile is currently running. Takes 10 seconds.
  230. # Returns the process-ID of the currently running POPFile, undef if none.
  231. #
  232. # ---------------------------------------------------------------------------------------------
  233. sub live_check_
  234. {
  235.     my ( $self ) = @_;
  236.  
  237.     if ( $self->check_pid_() ) {
  238.  
  239.         my $oldpid = $self->get_pid_();
  240.  
  241.         my $error = "\n\nA copy of POPFile appears to be running.\n Attempting to signal the previous copy.\n Waiting " . ($self->{pid_delay__} * 2) . " seconds for a reply.\n";
  242.  
  243.         $self->delete_pid_();
  244.  
  245.         print STDERR $error;
  246.  
  247.         select(undef, undef, undef, ($self->{pid_delay__} * 2));
  248.  
  249.         my $pid = $self->get_pid_();
  250.  
  251.         if (defined($pid)) {
  252.             $error = "\n A copy of POPFile is running.\n It has signaled that it is alive with process ID: $pid\n";
  253.             print STDERR $error;
  254.             return $pid;
  255.         } else {
  256.             print STDERR "\nThe other POPFile ($oldpid) failed to signal back, starting new copy ($$)\n";
  257.     }
  258.     }
  259.     return undef;
  260. }
  261.  
  262. # ---------------------------------------------------------------------------------------------
  263. #
  264. # check_pid_
  265. #
  266. # returns 1 if the pid file exists, 0 otherwise
  267. #
  268. # ---------------------------------------------------------------------------------------------
  269.  
  270. sub check_pid_
  271. {
  272.     my ( $self ) = @_;
  273.     return (-e $self->{pid_file__});
  274. }
  275.  
  276. # ---------------------------------------------------------------------------------------------
  277. #
  278. # get_pid_
  279. #
  280. # returns the pidfile proccess ID if a pid file is present, undef otherwise (0 might be a valid PID)
  281. #
  282. # ---------------------------------------------------------------------------------------------
  283. sub get_pid_
  284. {
  285.     my ( $self ) = @_;
  286.  
  287.     if (open PID, $self->{pid_file__}) {
  288.         my $pid = <PID>;
  289.         $pid =~ s/[\r\n]//g;
  290.         close PID;
  291.         return $pid;
  292.     }
  293.  
  294.     return undef;
  295. }
  296.  
  297. # ---------------------------------------------------------------------------------------------
  298. #
  299. # write_pid_
  300. #
  301. # writes the current process-ID into the pid file
  302. #
  303. # ---------------------------------------------------------------------------------------------
  304. sub write_pid_
  305. {
  306.     my ( $self ) = @_;
  307.  
  308.     if ( open PID, ">$self->{pid_file__}" ) {
  309.         print PID "$$\n";
  310.         close PID;
  311.     }
  312. }
  313.  
  314. # ---------------------------------------------------------------------------------------------
  315. #
  316. # delete_pid_
  317. #
  318. # deletes the pid file
  319. #
  320. # ---------------------------------------------------------------------------------------------
  321. sub delete_pid_
  322. {
  323.     my ( $self ) = @_;
  324.  
  325.     unlink( $self->{pid_file__} );
  326. }
  327.  
  328. # ---------------------------------------------------------------------------------------------
  329. #
  330. # parse_command_line - Parse ARGV
  331. #
  332. # The arguments are the keys of the configuration hash.  Any argument that is not already
  333. # defined in the hash generates an error, there must be an even number of ARGV elements because
  334. # each command argument has to have a value.
  335. #
  336. # ---------------------------------------------------------------------------------------------
  337. sub parse_command_line
  338. {
  339.     my ( $self ) = @_;
  340.  
  341.     # Options from the command line specified with the --set parameter
  342.  
  343.     my @set_options;
  344.  
  345.     # The following command line options are supported:
  346.     #
  347.     # --set          Permanently sets a configuration item for the current user
  348.     # --             Everything after this point is an old style POPFile option
  349.     #
  350.     # So its possible to do
  351.     #
  352.     # --set bayes_param=value --set=-bayes_param=value --set -bayes_param=value -- -bayes_param value
  353.  
  354.     if ( !GetOptions( "set=s" => \@set_options ) ) {
  355.         return 0;
  356.     }
  357.  
  358.     # Join together the options specified with --set and those after the --, the
  359.     # options in @set_options are going to be of the form foo=bar and hence need to
  360.     # be split into foo bar
  361.  
  362.     my @options;
  363.  
  364.     for my $i (0..$#set_options) {
  365.         $set_options[$i] =~ /-?(.+)=(.+)/;
  366.  
  367.     if ( !defined( $1 ) ) {
  368.             print STDERR "\nBad option: $set_options[$i]\n";
  369.             return 0;
  370.     }
  371.  
  372.         push @options, ("-$1");
  373.         if ( defined( $2 ) ) {
  374.             push @options, ($2);
  375.     }
  376.     }
  377.  
  378.     push @options, @ARGV;
  379.  
  380.     if ( $#options >= 0 )  {
  381.         my $i = 0;
  382.  
  383.         while ( $i <= $#options )  {
  384.             # A command line argument must start with a -
  385.  
  386.             if ( $options[$i] =~ /^-(.+)$/ ) {
  387.                 my $parameter = $self->upgrade_parameter__($1);
  388.  
  389.                 if ( defined($self->{configuration_parameters__}{$parameter}) ) {
  390.                     if ( $i < $#options ) {
  391.                         $self->parameter( $parameter, $options[$i+1] );
  392.                         $i += 2;
  393.                     } else {
  394.                         print STDERR "\nMissing argument for $options[$i]\n";
  395.                         return 0;
  396.                     }
  397.                 } else {
  398.                     print STDERR "\nUnknown option $options[$i]\n";
  399.                     return 0;
  400.                 }
  401.             } else {
  402.                 print STDERR "\nExpected a command line option and got $options[$i]\n";
  403.                 return 0;
  404.             }
  405.         }
  406.     }
  407.  
  408.     return 1;
  409. }
  410.  
  411. # ---------------------------------------------------------------------------------------------
  412. #
  413. # upgrade_parameter__
  414. #
  415. # Given a parameter from either command line or from the configuration file return the
  416. # upgraded version (e.g. the old port parameter becomes pop3_port
  417. #
  418. # ---------------------------------------------------------------------------------------------
  419.  
  420. sub upgrade_parameter__
  421. {
  422.     my ( $self, $parameter ) = @_;
  423.  
  424.     # This table maps from the old parameter to the new one, for
  425.     # example the old xpl parameter which controls insertion of the
  426.     # X-POPFile-Link header in email is now called GLOBAL_xpl and is
  427.     # accessed through POPFile::Module::global_config_ The old piddir
  428.     # parameter is now config_piddir and is accessed through either
  429.     # config_ if accessed from the config module or through
  430.     # module_config_ from outside
  431.  
  432.     my %upgrades = ( # PROFILE BLOCK START
  433.  
  434.                      # Parameters that are now handled by Classifier::Bayes
  435.  
  436.                      'corpus',                   'bayes_corpus',
  437.                      'unclassified_probability', 'bayes_unclassified_probability',
  438.  
  439.                      # Parameters that are now handled by POPFile::Configuration
  440.  
  441.                      'piddir',                   'config_piddir',
  442.  
  443.                      # Parameters that are now global to POPFile
  444.  
  445.                      'debug',                    'GLOBAL_debug',
  446.                      'msgdir',                   'GLOBAL_msgdir',
  447.                      'timeout',                  'GLOBAL_timeout',
  448.  
  449.                      # Parameters that are now handled by POPFile::Logger
  450.  
  451.                      'logdir',                   'logger_logdir',
  452.  
  453.                      # Parameters that are now handled by Proxy::POP3
  454.  
  455.                      'localpop',                 'pop3_local',
  456.                      'port',                     'pop3_port',
  457.                      'sport',                    'pop3_secure_port',
  458.                      'server',                   'pop3_secure_server',
  459.                      'separator',                'pop3_separator',
  460.                      'toptoo',                   'pop3_toptoo',
  461.  
  462.                      # Parameters that are now handled by UI::HTML
  463.  
  464.                      'language',                 'html_language',
  465.                      'last_reset',               'html_last_reset',
  466.                      'last_update_check',        'html_last_update_check',
  467.                      'localui',                  'html_local',
  468.                      'page_size',                'html_page_size',
  469.                      'password',                 'html_password',
  470.                      'send_stats',               'html_send_stats',
  471.                      'skin',                     'html_skin',
  472.                      'test_language',            'html_test_language',
  473.                      'update_check',             'html_update_check',
  474.                      'ui_port',                  'html_port',
  475.  
  476.                      # Parameters the have moved from the UI::HTML to
  477.                      # POPFile::History
  478.  
  479.                      'archive',                  'history_archive',
  480.                      'archive_classes',          'history_archive_classes',
  481.                      'archive_dir',              'history_archive_dir',
  482.                      'history_days',             'history_history_days',
  483.                      'html_archive',             'history_archive',
  484.                      'html_archive_classes',     'history_archive_classes',
  485.                      'html_archive_dir',         'history_archive_dir',
  486.                      'html_history_days',        'history_history_days',
  487.  
  488.     ); # PROFILE BLOCK STOP
  489.  
  490.     if ( defined( $upgrades{$parameter} ) ) {
  491.         return $upgrades{$parameter};
  492.     } else {
  493.         return $parameter;
  494.     }
  495. }
  496.  
  497. # ---------------------------------------------------------------------------------------------
  498. #
  499. # load_configuration
  500. #
  501. # Loads the current configuration of popfile into the configuration hash from a local file.
  502. # The format is a very simple set of lines containing a space separated name and value pair
  503. #
  504. # ---------------------------------------------------------------------------------------------
  505. sub load_configuration
  506. {
  507.     my ( $self ) = @_;
  508.  
  509.     $self->{started__} = 1;
  510.  
  511.     if ( open CONFIG, '<' . $self->get_user_path( 'popfile.cfg' ) ) {
  512.         while ( <CONFIG> ) {
  513.             s/(\015|\012)//g;
  514.             if ( /(\S+) (.+)?/ ) {
  515.                 my $parameter = $1;
  516.                 my $value     = $2;
  517.                 $value = '' if !defined( $value );
  518.  
  519.                 $parameter = $self->upgrade_parameter__($parameter);
  520.  
  521.                 if ( defined( $self->{configuration_parameters__}{$parameter} ) ) {
  522.                     $self->{configuration_parameters__}{$parameter}{value} = $value;
  523.                 } else {
  524.                     $self->{deprecated_parameters__}{$parameter} = $value;
  525.                 }
  526.             }
  527.         }
  528.  
  529.         close CONFIG;
  530.     }
  531.  
  532.     $self->{save_needed__} = 0;
  533. }
  534.  
  535. # ---------------------------------------------------------------------------------------------
  536. #
  537. # save_configuration
  538. #
  539. # Saves the current configuration of popfile from the configuration hash to a local file.
  540. #
  541. # ---------------------------------------------------------------------------------------------
  542. sub save_configuration
  543. {
  544.     my ( $self ) = @_;
  545.  
  546.     if ( $self->{save_needed__} == 0 ) {
  547.         return;
  548.     }
  549.  
  550.     if ( open CONFIG, '>' . $self->get_user_path( 'popfile.cfg' ) ) {
  551.         $self->{save_needed__} = 0;
  552.  
  553.         foreach my $key (sort keys %{$self->{configuration_parameters__}}) {
  554.             print CONFIG "$key $self->{configuration_parameters__}{$key}{value}\n";
  555.         }
  556.  
  557.         close CONFIG;
  558.     }
  559. }
  560.  
  561. # ---------------------------------------------------------------------------------------------
  562. #
  563. # get_user_path, get_root_path
  564. #
  565. # Resolve a path relative to POPFILE_USER or POPFILE_ROOT
  566. #
  567. # $path              The path to resolve
  568. # $sandbox           Set to 1 if this path must be sandboxed (i.e. absolute
  569. #                    paths and paths containing .. are not accepted).
  570. #
  571. # ---------------------------------------------------------------------------------------------
  572. sub get_user_path
  573. {
  574.     my ( $self, $path, $sandbox ) = @_;
  575.  
  576.     return $self->path_join__( $self->{popfile_user__}, $path, $sandbox );
  577. }
  578.  
  579. sub get_root_path
  580. {
  581.     my ( $self, $path, $sandbox ) = @_;
  582.  
  583.     return $self->path_join__( $self->{popfile_root__}, $path, $sandbox );
  584. }
  585.  
  586. # ---------------------------------------------------------------------------------------------
  587. #
  588. # path_join__
  589. #
  590. # Join two paths togther
  591. #
  592. # $left              The LHS
  593. # $right             The RHS
  594. # $sandbox           Set to 1 if this path must be sandboxed (i.e. absolute
  595. #                    paths and paths containing .. are not accepted).
  596. #
  597. # ---------------------------------------------------------------------------------------------
  598. sub path_join__
  599. {
  600.     my ( $self, $left, $right, $sandbox ) = @_;
  601.  
  602.     $sandbox = 1 if ( !defined( $sandbox ) );
  603.  
  604.     if ( ( $right =~ /^\// ) ||
  605.          ( $right =~ /^[A-Za-z]:[\/\\]/ ) ||
  606.          ( $right =~ /\\\\/ ) ) {
  607.         if ( $sandbox ) {
  608.             $self->log_( 0, "Attempt to access path $right outside sandbox" );
  609.             return undef;
  610.         } else {
  611.             return $right;
  612.         }
  613.     }
  614.  
  615.     if ( $sandbox && ( $right =~ /\.\./ ) ) {
  616.         $self->log_( 0, "Attempt to access path $right outside sandbox" );
  617.         return undef;
  618.     }
  619.  
  620.     $left  =~ s/\/$//;
  621.     $right =~ s/^\///;
  622.  
  623.     return "$left/$right";
  624. }
  625.  
  626. # ---------------------------------------------------------------------------------------------
  627. #
  628. # parameter
  629. #
  630. # Gets or sets a parameter
  631. #
  632. # $name          Name of the parameter to get or set
  633. # $value         Optional value to set the parameter to
  634. #
  635. # Always returns the current value of the parameter
  636. #
  637. # ---------------------------------------------------------------------------------------------
  638. sub parameter
  639. {
  640.     my ( $self, $name, $value ) = @_;
  641.  
  642.     if ( defined( $value ) ) {
  643.         $self->{save_needed__} = 1;
  644.         $self->{configuration_parameters__}{$name}{value} = $value;
  645.         if ( $self->{started__} == 0 ) {
  646.             $self->{configuration_parameters__}{$name}{default} = $value;
  647.         }
  648.     }
  649.  
  650.     return $self->{configuration_parameters__}{$name}{value};
  651. }
  652.  
  653. # ---------------------------------------------------------------------------------------------
  654. #
  655. # is_default
  656. #
  657. # Returns whether the parameter has the default value or not
  658. #
  659. # $name          Name of the parameter
  660. #
  661. # Returns 1 if the parameter still has its default value
  662. #
  663. # ---------------------------------------------------------------------------------------------
  664. sub is_default
  665. {
  666.     my ( $self, $name ) = @_;
  667.  
  668.     return ( $self->{configuration_parameters__}{$name}{value} eq
  669.              $self->{configuration_parameters__}{$name}{default} );
  670. }
  671.  
  672. # GETTERS
  673.  
  674. sub configuration_parameters
  675. {
  676.     my ( $self ) = @_;
  677.  
  678.     return sort keys %{$self->{configuration_parameters__}};
  679. }
  680.  
  681. sub deprecated_parameter
  682. {
  683.     my ( $self, $name ) = @_;
  684.  
  685.     return $self->{deprecated_parameters__}{$name};
  686. }
  687.  
  688. 1;
  689.  
  690.