diff options
Diffstat (limited to 'rt/lib/RT.pm')
| -rw-r--r-- | rt/lib/RT.pm | 619 |
1 files changed, 218 insertions, 401 deletions
diff --git a/rt/lib/RT.pm b/rt/lib/RT.pm index 3ed85afc0..033e5e607 100644 --- a/rt/lib/RT.pm +++ b/rt/lib/RT.pm @@ -1,8 +1,8 @@ # BEGIN BPS TAGGED BLOCK {{{ # # COPYRIGHT: -# -# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC +# +# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC # <jesse@bestpractical.com> # # (Except where explicitly superseded by other copyright notices) @@ -45,79 +45,61 @@ # those contributions and any derivatives thereof. # # END BPS TAGGED BLOCK }}} - -use strict; -use warnings; - package RT; - - -use File::Spec (); -use Cwd (); - -use vars qw($Config $System $SystemUser $Nobody $Handle $Logger $_INSTALL_MODE); - -our $VERSION = '3.8.7'; - - - -our $BasePath = '/opt/rt3'; -our $EtcPath = 'etc'; -our $BinPath = 'bin'; -our $SbinPath = 'sbin'; -our $VarPath = 'var'; -our $PluginPath = 'plugins'; -our $LocalPath = 'local'; -our $LocalEtcPath = 'local/etc'; -our $LocalLibPath = 'local/lib'; -our $LocalLexiconPath = 'local/po'; -our $LocalPluginPath = $LocalPath."/plugins"; - +use strict; +use RT::I18N; +use RT::CurrentUser; +use RT::System; + +use vars qw($VERSION $System $SystemUser $Nobody $Handle $Logger + $CORE_CONFIG_FILE + $SITE_CONFIG_FILE + $BasePath + $EtcPath + $VarPath + $LocalPath + $LocalEtcPath + $LocalLexiconPath + $LogDir + $BinPath + $MasonComponentRoot + $MasonLocalComponentRoot + $MasonDataDir + $MasonSessionDir +); + +$VERSION = '3.6.10'; +$CORE_CONFIG_FILE = "/opt/rt3/etc/RT_Config.pm"; +$SITE_CONFIG_FILE = "/opt/rt3/etc/RT_SiteConfig.pm"; + + + +$BasePath = '/opt/rt3'; + +$EtcPath = '/opt/rt3/etc'; +$BinPath = '/opt/rt3/bin'; +$VarPath = '/opt/rt3/var'; +$LocalPath = '/opt/rt3/local'; +$LocalEtcPath = '/opt/rt3/local/etc'; +$LocalLexiconPath = '/opt/rt3/local/po'; # $MasonComponentRoot is where your rt instance keeps its mason html files -our $MasonComponentRoot = 'share/html'; +$MasonComponentRoot = '/var/www/freeside/rt'; # $MasonLocalComponentRoot is where your rt instance keeps its site-local # mason html files. -our $MasonLocalComponentRoot = 'local/html'; +$MasonLocalComponentRoot = '/opt/rt3/local/html'; # $MasonDataDir Where mason keeps its datafiles -our $MasonDataDir = 'var/mason_data'; +$MasonDataDir = '/usr/local/etc/freeside/masondata'; # RT needs to put session data (for preserving state between connections # via the web interface) -our $MasonSessionDir = 'var/session_data'; - -unless ( File::Spec->file_name_is_absolute($EtcPath) ) { - -# if BasePath exists and is absolute, we won't infer it from $INC{'RT.pm'}. -# otherwise RT.pm will make src dir(where we configure RT) be the BasePath -# instead of the --prefix one - unless ( -d $BasePath && File::Spec->file_name_is_absolute($BasePath) ) { - my $pm_path = ( File::Spec->splitpath( $INC{'RT.pm'} ) )[1]; +$MasonSessionDir = '/opt/rt3/var/session_data'; - # need rel2abs here is to make sure path is absolute, since $INC{'RT.pm'} - # is not always absolute - $BasePath = - File::Spec->rel2abs( - File::Spec->catdir( $pm_path, File::Spec->updir ) ); - } - - $BasePath = Cwd::realpath( $BasePath ); - - for my $path ( qw/EtcPath BinPath SbinPath VarPath LocalPath LocalEtcPath - LocalLibPath LocalLexiconPath PluginPath LocalPluginPath - MasonComponentRoot MasonLocalComponentRoot MasonDataDir - MasonSessionDir/ ) { - no strict 'refs'; - # just change relative ones - $$path = File::Spec->catfile( $BasePath, $$path ) - unless File::Spec->file_name_is_absolute( $$path ); - } -} =head1 NAME @@ -130,14 +112,12 @@ A fully featured request tracker package =head1 DESCRIPTION -=head2 INITIALIZATION - =head2 LoadConfig Load RT's config file. First, the site configuration file -(F<RT_SiteConfig.pm>) is loaded, in order to establish overall site +(C<RT_SiteConfig.pm>) is loaded, in order to establish overall site settings like hostname and name of RT instance. Then, the core -configuration file (F<RT_Config.pm>) is loaded to set fallback values +configuration file (C<RT_Config.pm>) is loaded to set fallback values for all settings; it bases some values on settings from the site configuration file. @@ -148,33 +128,59 @@ have not been set already. =cut sub LoadConfig { - require RT::Config; - $Config = new RT::Config; - $Config->LoadConfigs; - require RT::I18N; + local *Set = sub { $_[0] = $_[1] unless defined $_[0] }; + + my $username = getpwuid($>); + my $group = getgrgid($(); + my $message = <<EOF; + +RT couldn't load RT config file %s as: + user: $username + group: $group + +The file is owned by user %s and group %s. + +This usually means that the user/group your webserver is running +as cannot read the file. Be careful not to make the permissions +on this file too liberal, because it contains database passwords. +You may need to put the webserver user in the appropriate group +(%s) or change permissions be able to run succesfully. +EOF + + + if ( -f "$SITE_CONFIG_FILE" ) { + eval { require $SITE_CONFIG_FILE }; + if ($@) { + my ($fileuid,$filegid) = (stat($SITE_CONFIG_FILE))[4,5]; + my $fileusername = getpwuid($fileuid); + my $filegroup = getgrgid($filegid); + my $errormessage = sprintf($message, $SITE_CONFIG_FILE, + $fileusername, $filegroup, $filegroup); + die ("$errormessage\n$@"); + } + } + eval { require $CORE_CONFIG_FILE }; + if ($@) { + my ($fileuid,$filegid) = (stat($CORE_CONFIG_FILE))[4,5]; + my $fileusername = getpwuid($fileuid); + my $filegroup = getgrgid($filegid); + my $errormessage = sprintf($message, $CORE_CONFIG_FILE, + $fileusername, $filegroup, $filegroup); + die ("$errormessage\n$@") + } # RT::Essentials mistakenly recommends that WebPath be set to '/'. # If the user does that, do what they mean. $RT::WebPath = '' if ($RT::WebPath eq '/'); - # fix relative LogDir and GnuPG homedir - unless ( File::Spec->file_name_is_absolute( $Config->Get('LogDir') ) ) { - $Config->Set( LogDir => - File::Spec->catfile( $BasePath, $Config->Get('LogDir') ) ); - } + $ENV{'TZ'} = $RT::Timezone if ($RT::Timezone); - my $gpgopts = $Config->Get('GnuPGOptions'); - unless ( File::Spec->file_name_is_absolute( $gpgopts->{homedir} ) ) { - $gpgopts->{homedir} = File::Spec->catfile( $BasePath, $gpgopts->{homedir} ); - } - RT::I18N->Init; } =head2 Init -L<Connect to the database /ConnectToDatabase>, L<initilizes system objects /InitSystemObjects>, -L<preloads classes /InitClasses> and L<set up logging /InitLogging>. +Conenct to the database, set up logging. =cut @@ -182,34 +188,41 @@ sub Init { CheckPerlRequirements(); - InitPluginPaths(); - #Get a database connection ConnectToDatabase(); - InitSystemObjects(); + + #RT's system user is a genuine database user. its id lives here + $SystemUser = new RT::CurrentUser(); + $SystemUser->LoadByName('RT_System'); + + #RT's "nobody user" is a genuine database user. its ID lives here. + $Nobody = new RT::CurrentUser(); + $Nobody->LoadByName('Nobody'); + + $System = RT::System->new(); + InitClasses(); InitLogging(); - InitPlugins(); - RT->Config->PostLoadCheck; - } + =head2 ConnectToDatabase -Get a database connection. See also </Handle>. +Get a database connection =cut sub ConnectToDatabase { require RT::Handle; - $Handle = new RT::Handle unless $Handle; - $Handle->Connect; - return $Handle; + unless ($Handle && $Handle->dbh && $Handle->dbh->ping) { + $Handle = RT::Handle->new(); + } + $Handle->Connect(); } =head2 InitLogging -Create the Logger object and set up signal handlers. +Create the RT::Logger object. =cut @@ -218,141 +231,112 @@ sub InitLogging { # We have to set the record separator ($, man perlvar) # or Log::Dispatch starts getting # really pissy, as some other module we use unsets it. + $, = ''; use Log::Dispatch 1.6; - my %level_to_num = ( - map( { $_ => } 0..7 ), - debug => 0, - info => 1, - notice => 2, - warning => 3, - error => 4, 'err' => 4, - critical => 5, crit => 5, - alert => 6, - emergency => 7, emerg => 7, - ); + unless ($RT::Logger) { - unless ( $RT::Logger ) { + $RT::Logger = Log::Dispatch->new(); - $RT::Logger = Log::Dispatch->new; + my $simple_cb = sub { + # if this code throw any warning we can get segfault + no warnings; - my $stack_from_level; - if ( $stack_from_level = RT->Config->Get('LogStackTraces') ) { - # if option has old style '\d'(true) value - $stack_from_level = 0 if $stack_from_level =~ /^\d+$/; - $stack_from_level = $level_to_num{ $stack_from_level } || 0; - } else { - $stack_from_level = 99; # don't log - } + my %p = @_; - my $simple_cb = sub { - # if this code throw any warning we can get segfault - no warnings; - my %p = @_; - - # skip Log::* stack frames - my $frame = 0; - $frame++ while caller($frame) && caller($frame) =~ /^Log::/; - my ($package, $filename, $line) = caller($frame); - - $p{'message'} =~ s/(?:\r*\n)+$//; - return "[". gmtime(time) ."] [". $p{'level'} ."]: " - . $p{'message'} ." ($filename:$line)\n"; - }; - - my $syslog_cb = sub { - # if this code throw any warning we can get segfault - no warnings; - my %p = @_; - - my $frame = 0; # stack frame index - # skip Log::* stack frames - $frame++ while caller($frame) && caller($frame) =~ /^Log::/; - my ($package, $filename, $line) = caller($frame); - - # syswrite() cannot take utf8; turn it off here. - Encode::_utf8_off($p{message}); - - $p{message} =~ s/(?:\r*\n)+$//; - if ($p{level} eq 'debug') { - return "$p{message}\n"; - } else { - return "$p{message} ($filename:$line)\n"; - } - }; + my $frame = 0; # stack frame index + # skip Log::* stack frames + $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ ); - my $stack_cb = sub { - no warnings; - my %p = @_; - return $p{'message'} unless $level_to_num{ $p{'level'} } >= $stack_from_level; - - require Devel::StackTrace; - my $trace = Devel::StackTrace->new( ignore_class => [ 'Log::Dispatch', 'Log::Dispatch::Base' ] ); - return $p{'message'} . $trace->as_string; + my ($package, $filename, $line) = caller($frame); + $p{message} =~ s/(?:\r*\n)+$//; + my $str = "[".gmtime(time)."] [".$p{level}."]: $p{message} ($filename:$line)\n"; + if( $RT::LogStackTraces ) { + $str .= "\nStack trace:\n"; # skip calling of the Log::* subroutins - my $frame = 0; - $frame++ while caller($frame) && caller($frame) =~ /^Log::/; - $frame++ while caller($frame) && (caller($frame))[3] =~ /^Log::/; - - $p{'message'} .= "\nStack trace:\n"; + $frame++ while( caller($frame) && (caller($frame))[3] =~ /^Log::/ ); while( my ($package, $filename, $line, $sub) = caller($frame++) ) { - $p{'message'} .= "\t$sub(...) called at $filename:$line\n"; - } - return $p{'message'}; - }; - - if ( $Config->Get('LogToFile') ) { - my ($filename, $logdir) = ( - $Config->Get('LogToFileNamed') || 'rt.log', - $Config->Get('LogDir') || File::Spec->catdir( $VarPath, 'log' ), - ); - if ( $filename =~ m![/\\]! ) { # looks like an absolute path. - ($logdir) = $filename =~ m{^(.*[/\\])}; - } - else { - $filename = File::Spec->catfile( $logdir, $filename ); + $str .= "\t". $sub ."() called at $filename:$line\n"; } + } + return $str; + }; - unless ( -d $logdir && ( ( -f $filename && -w $filename ) || -w $logdir ) ) { - # localizing here would be hard when we don't have a current user yet - die "Log file '$filename' couldn't be written or created.\n RT can't run."; - } + my $syslog_cb = sub { + my %p = @_; + + my $frame = 0; # stack frame index + # skip Log::* stack frames + $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ ); + my ($package, $filename, $line) = caller($frame); + + # syswrite() cannot take utf8; turn it off here. + Encode::_utf8_off($p{message}); - require Log::Dispatch::File; - $RT::Logger->add( Log::Dispatch::File->new - ( name=>'file', - min_level=> $Config->Get('LogToFile'), - filename=> $filename, - mode=>'append', - callbacks => [ $simple_cb, $stack_cb ], - )); + $p{message} =~ s/(?:\r*\n)+$//; + if ($p{level} eq 'debug') { + return "$p{message}\n" + } else { + return "$p{message} ($filename:$line)\n" + } + }; + + if ($RT::LogToFile) { + my ($filename, $logdir); + if ($RT::LogToFileNamed =~ m![/\\]!) { + # looks like an absolute path. + $filename = $RT::LogToFileNamed; + ($logdir) = $RT::LogToFileNamed =~ m!^(.*[/\\])!; } - if ( $Config->Get('LogToScreen') ) { - require Log::Dispatch::Screen; - $RT::Logger->add( Log::Dispatch::Screen->new - ( name => 'screen', - min_level => $Config->Get('LogToScreen'), - callbacks => [ $simple_cb, $stack_cb ], - stderr => 1, - )); + else { + $filename = "$RT::LogDir/$RT::LogToFileNamed"; + $logdir = $RT::LogDir; } - if ( $Config->Get('LogToSyslog') ) { - require Log::Dispatch::Syslog; - $RT::Logger->add(Log::Dispatch::Syslog->new - ( name => 'syslog', - ident => 'RT', - min_level => $Config->Get('LogToSyslog'), - callbacks => [ $syslog_cb, $stack_cb ], - stderr => 1, - $Config->Get('LogToSyslogConf'), - )); + + unless ( -d $logdir && ( ( -f $filename && -w $filename ) || -w $logdir ) ) { + # localizing here would be hard when we don't have a current user yet + die "Log file $filename couldn't be written or created.\n RT can't run."; } + + package Log::Dispatch::File; + require Log::Dispatch::File; + $RT::Logger->add(Log::Dispatch::File->new + ( name=>'rtlog', + min_level=> $RT::LogToFile, + filename=> $filename, + mode=>'append', + callbacks => $simple_cb, + )); + } + if ($RT::LogToScreen) { + package Log::Dispatch::Screen; + require Log::Dispatch::Screen; + $RT::Logger->add(Log::Dispatch::Screen->new + ( name => 'screen', + min_level => $RT::LogToScreen, + callbacks => $simple_cb, + stderr => 1, + )); + } + if ($RT::LogToSyslog) { + package Log::Dispatch::Syslog; + require Log::Dispatch::Syslog; + $RT::Logger->add(Log::Dispatch::Syslog->new + ( name => 'syslog', + ident => 'RT', + min_level => $RT::LogToSyslog, + callbacks => $syslog_cb, + stderr => 1, + @RT::LogToSyslogConf + )); + } + } +# {{{ Signal handlers -# Signal handlers ## This is the default handling of warnings and die'ings in the code ## (including other used modules - maybe except for errors catched by ## Mason). It will log all problems through the standard logging @@ -371,15 +355,16 @@ sub InitLogging { #When we call die, trap it and log->crit with the value of the die. - $SIG{__DIE__} = sub { - # if we are not in eval and perl is not parsing code - # then rollback transactions and log RT error - unless ($^S || !defined $^S ) { - $RT::Handle->Rollback(1) if $RT::Handle; - $RT::Logger->crit("$_[0]") if $RT::Logger; - } - die $_[0]; - }; +$SIG{__DIE__} = sub { + unless ($^S || !defined $^S ) { + $RT::Handle->Rollback(); + $RT::Logger->crit("$_[0]"); + } + die $_[0]; +}; + +# }}} + } @@ -415,9 +400,10 @@ EOF } } + =head2 InitClasses -Load all modules that define base classes. +Load all modules that define base classes =cut @@ -440,8 +426,6 @@ sub InitClasses { require RT::ObjectCustomFields; require RT::ObjectCustomFieldValues; require RT::Attributes; - require RT::Dashboard; - require RT::Approval; # on a cold server (just after restart) people could have an object # in the session, as we deserialize it so we never call constructor @@ -468,196 +452,21 @@ sub InitClasses { ); } -=head2 InitSystemObjects - -Initializes system objects: C<$RT::System>, C<$RT::SystemUser> -and C<$RT::Nobody>. - -=cut - -sub InitSystemObjects { - - #RT's system user is a genuine database user. its id lives here - require RT::CurrentUser; - $SystemUser = new RT::CurrentUser; - $SystemUser->LoadByName('RT_System'); - - #RT's "nobody user" is a genuine database user. its ID lives here. - $Nobody = new RT::CurrentUser; - $Nobody->LoadByName('Nobody'); - - require RT::System; - $System = RT::System->new( $SystemUser ); -} - -=head1 CLASS METHODS - -=head2 Config - -Returns the current L<config object RT::Config>, but note that -you must L<load config /LoadConfig> first otherwise this method -returns undef. - -Method can be called as class method. - -=cut - -sub Config { return $Config } - -=head2 DatabaseHandle - -Returns the current L<database handle object RT::Handle>. - -See also L</ConnectToDatabase>. - -=cut - -sub DatabaseHandle { return $Handle } - -=head2 Logger - -Returns the logger. See also L</InitLogging>. - -=cut - -sub Logger { return $Logger } - -=head2 System - -Returns the current L<system object RT::System>. See also -L</InitSystemObjects>. - -=cut - -sub System { return $System } - -=head2 SystemUser - -Returns the system user's object, it's object of -L<RT::CurrentUser> class that represents the system. See also -L</InitSystemObjects>. - -=cut - -sub SystemUser { return $SystemUser } - -=head2 Nobody - -Returns object of Nobody. It's object of L<RT::CurrentUser> class -that represents a user who can own ticket and nothing else. See -also L</InitSystemObjects>. - -=cut - -sub Nobody { return $Nobody } - -=head2 Plugins - -Returns a listref of all Plugins currently configured for this RT instance. -You can define plugins by adding them to the @Plugins list in your RT_SiteConfig - -=cut - -our @PLUGINS = (); -sub Plugins { - my $self = shift; - unless (@PLUGINS) { - $self->InitPluginPaths; - @PLUGINS = $self->InitPlugins; - } - return \@PLUGINS; -} - -=head2 PluginDirs - -Takes optional subdir (e.g. po, lib, etc.) and return plugins' dirs that exist. - -This code chacke plugins names or anything else and required when main config -is loaded to load plugins' configs. - -=cut - -sub PluginDirs { - my $self = shift; - my $subdir = shift; - - require RT::Plugin; - - my @res; - foreach my $plugin (grep $_, RT->Config->Get('Plugins')) { - my $path = RT::Plugin->new( name => $plugin )->Path( $subdir ); - next unless -d $path; - push @res, $path; - } - return @res; -} - -=head2 InitPluginPaths - -Push plugins' lib paths into @INC right after F<local/lib>. -In case F<local/lib> isn't in @INC, append them to @INC - -=cut - -sub InitPluginPaths { - my $self = shift || __PACKAGE__; - - my @lib_dirs = $self->PluginDirs('lib'); - - my @tmp_inc; - my $added; - for (@INC) { - if ( Cwd::realpath($_) eq $RT::LocalLibPath) { - push @tmp_inc, $_, @lib_dirs; - $added = 1; - } else { - push @tmp_inc, $_; - } - } - - # append @lib_dirs in case $RT::LocalLibPath isn't in @INC - push @tmp_inc, @lib_dirs unless $added; - - my %seen; - @INC = grep !$seen{$_}++, @tmp_inc; -} - -=head2 InitPlugins - -Initialze all Plugins found in the RT configuration file, setting up their lib and HTML::Mason component roots. +# }}} -=cut - -sub InitPlugins { - my $self = shift; - my @plugins; - require RT::Plugin; - foreach my $plugin (grep $_, RT->Config->Get('Plugins')) { - $plugin->require; - die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR); - push @plugins, RT::Plugin->new(name =>$plugin); - } - return @plugins; -} +sub SystemUser { + return($SystemUser); +} -sub InstallMode { - my $self = shift; - if (@_) { - $_INSTALL_MODE = shift; - if($_INSTALL_MODE) { - require RT::CurrentUser; - $SystemUser = RT::CurrentUser->new(); - } - } - return $_INSTALL_MODE; +sub Nobody { + return ($Nobody); } - =head1 BUGS -Please report them to rt-bugs@bestpractical.com, if you know what's -broken and have at least some idea of what needs to be fixed. +Please report them to rt-bugs@fsck.com, if you know what's broken and have at least +some idea of what needs to be fixed. If you're not sure what's going on, report them rt-devel@lists.bestpractical.com. @@ -666,6 +475,14 @@ If you're not sure what's going on, report them rt-devel@lists.bestpractical.com L<RT::StyleGuide> L<DBIx::SearchBuilder> +=begin testing + +ok ($RT::Nobody->Name() eq 'Nobody', "Nobody is nobody"); +ok ($RT::Nobody->Name() ne 'root', "Nobody isn't named root"); +ok ($RT::SystemUser->Name() eq 'RT_System', "The system user is RT_System"); +ok ($RT::SystemUser->Name() ne 'noname', "The system user isn't noname"); + +=end testing =cut |
