# ================================================================== # File manager - enhanced web based file management system # # Website : http://gossamer-threads.com/ # Support : http://gossamer-threads.com/scripts/support/ # CVS Info : # Revision : $Id: FileMan.pm,v 1.121 2005/04/11 17:24:03 jagerman Exp $ # # Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved. # Redistribution in part or in whole strictly prohibited. Please # see LICENSE file for full details. # ================================================================== package GT::FileMan; #-------------------------------------------------------------------- use strict; use vars qw/@ISA $DEBUG $HAVE_GZIP $HAVE_AZIP $UNSAFE_PATH/; use GT::Base qw/:persist/; use GT::Template; use GT::FileMan::Commands; # Check if Compress::Zlib is available $HAVE_GZIP = eval { local $SIG{__DIE__}; require Compress::Zlib; 1; } ? 1 : 0; # Check if Archive::Zip is available $HAVE_AZIP = eval { local $SIG{__DIE__}; require Archive::Zip; 1; } ? 1 : 0; $DEBUG = 0; @ISA = qw/GT::FileMan::Commands GT::Base/; $UNSAFE_PATH = $^O =~ /mswin/i ? '(^|[/\\\\])\.\.?($|[/\\\\])' : '(^|/)\.\.?($|/)'; sub new { # ------------------------------------------------------------------ # Constructor # my ($class,%args) = @_; my $self = bless {%args}, ref $class || $class; $self->{cfg} = $self->load_config() if (!$self->{cfg}); $self->{cfg}->{winnt} = $^O eq 'MSWin32' ? 1 : 0; $self->{cfg}->{upload_chmod} ||= '644'; $self->{cfg}->{template_root} or die('You must pass in your template root !'); $self->{cfg}->{root_dir} or die('You must set your root dir !'); $self->{in} = new GT::CGI; $self->{cgi} = $self->{in}->get_hash; my $passwd_dir = $self->{passwd_dir}; if ($passwd_dir and !$self->{in}->cookie('def_passwd_dir')) { #store the password directory to cookie $passwd_dir = "$self->{cfg}->{root_dir}/$passwd_dir" if ($self->{cfg}->{passwd_dir_level}); # must be inside root directory (-e $passwd_dir and -w _) or die("$passwd_dir does not exist or not writeable"); print $self->{in}->header (-cookie => [ $self->{in}->cookie ( -name => 'def_passwd_dir', -value => $passwd_dir, -expires => '+5y') ]); } # Set our default working directory. $self->{work_path} = $self->{cgi}->{work_path}; if ($self->{cgi}->{def_load} and !$self->{cgi}->{work_path}) { $self->{work_path} = ($self->{in}->cookie('def_working_dir') eq '/') ? '' : $self->{in}->cookie('def_working_dir'); (!$self->{work_path} or ($self->{work_path} =~ m,^([-\w/. ]+)$, and $self->{work_path} !~ /$UNSAFE_PATH/)) or ($self->{work_path} = ''); } $self->{work_path} ||= ''; (!$self->{work_path} or ($self->{work_path} =~ m,^([-\w/. ]+)$, and $self->{work_path} !~ /$UNSAFE_PATH/)) or die ("work_path has invalid characters : $self->{work_path} "); -e "$self->{cfg}->{root_dir}/$self->{work_path}" or ($self->{work_path} = ''); $self->{http_ref} = $self->{in}->url (absolute => 0, query_string => 0); $self->{results} = ''; $self->{data} = {}; $self->{status} = ''; $self->{input} = ''; $self->{debug} and ($DEBUG = $self->{debug}); return $self; } sub process { # ------------------------------------------------------------------ my $self = shift; my $action = $self->{cgi}->{fdo} || $self->{cgi}->{cmd_do}; return $self->page("home.html") if (!$action or $action eq 'fileman'); my $command_enable = 1; # default is enable $command_enable = $self->{commands}->{$action} if (exists $self->{commands}->{$action}); # Determine what to do: if (exists $GT::FileMan::Commands::COMPILE{$action} and $command_enable) { $self->$action(); } else { die "Invalid action or command is disable : $action !"; } } sub page { # ------------------------------------------------------------------ # Print out the requested template # my ($self, $file, $args) = @_; $file ||= $self->{cgi}->{page}; print $self->{in}->header; my $template_path = ($self->{cgi}->{t}) ? "$self->{cfg}->{template_root}/$self->{cgi}->{t}" : $self->{cfg}->{template_root}; # Check the file name requested. "$template_path/$file" =~ /\\/ and return die "Invalid template '$file' requested (Invalid name)"; "$template_path/$file" =~ /$UNSAFE_PATH/ and return die "Invalid template '$file' requested (Invalid name)"; $file =~ m,^\s*/, and return die "Invalid template '$file' requested (Invalid name)"; -e "$template_path/$file" or return die "Invalid template '$template_path/$file' requested (File does not exist)"; -r _ or return die "Invalid template '$file' requested (Permission denied)"; # Make data available. foreach my $key (keys % {$self->{data}}) { exists $args->{$key} or $args->{$key} = $self->{data}->{$key}; } # Make cgi input available. foreach my $key (keys % {$self->{cgi}}) { exists $args->{$key} or $args->{$key} = $self->{cgi}->{$key}; } # Make commands available. my $count = 0; if ($self->{commands}) { #activate or deactivate the commands foreach my $key (keys % {$self->{commands}}) { exists $args->{$key} or $args->{$key} = $self->{commands}->{$key}; $count++; } } $args->{show_all} = '1' if ($count == 0); $args->{status} ||= $self->{status}; $args->{input} = $self->{input}; $args->{http_ref} = $self->{http_ref}; $args->{url_opts} = $self->{url_opts}; $args->{work_path} = $self->{work_path} || $self->{cgi}->{work_path}; $args->{template_root} = $self->{cfg}->{template_root}; $args->{root_dir} = $self->{cfg}->{root_dir}; $args->{html_url} = $self->{cfg}->{html_root_url}; $args->{root_url} = $self->{cfg}->{root_url}; $args->{root_select} = $self->{cfg}->{root_select} if ($self->{cfg}->{root_select}); $args->{session_id} = $self->{cfg}->{session_id} if ($self->{cfg}->{session_id}); $args->{user_sessions} = $self->{cfg}->{user_sessions} if ($self->{cfg}->{user_sessions}); $args->{username} = $self->{cfg}->{username} if ($self->{cfg}->{username}); $args->{multi} = $self->{cfg}->{multi} if ($self->{cfg}->{multi}); $args->{single} = $self->{cfg}->{single} if ($self->{cfg}->{single}); $args->{have_gzip} = $HAVE_GZIP; $args->{have_azip} = $HAVE_AZIP; $args->{srv_soft} = ($ENV{SERVER_SOFTWARE} =~ /Apache|Unix/)? 0 : 1 if ($ENV{SERVER_SOFTWARE}); $args->{position} = $self->{in}->cookie('readme_position') if ($args->{readme}); $args->{scheme} = $self->{in}->cookie('scheme') || 'fileman'; $args->{font} = $self->{in}->cookie('font') || ""; $args->{font} =~ s/[\'\"]/\'/g; # Used for HTML editor my $brws = $self->get_browser(); # Export home for using in auto generate HTML. GT::Template->parse ("$template_path/$file", { %$args, %$brws }, { print => 1 }); } sub get_browser { my ($self, $verify) = @_; my ($version, %brws); if ($ENV{HTTP_USER_AGENT} and $ENV{HTTP_USER_AGENT} =~ /MSIE (\d+(?:\.\d+)?)/i) { $version = $1; $brws{ie_version} = $version; } $brws{is_ie} = ($version and $version >= 5.5) ? 1 : 0; if ($ENV{HTTP_USER_AGENT} and $ENV{HTTP_USER_AGENT} =~ m{Mozilla/(\d+\.\d+)\s+\([^)]*rv:(\d+\.\d+)\)}) { if ($1 >= 5.0) { $brws{is_mozilla} = 1; $brws{mozilla_version} = $2; } } if ( $verify ) { ($brws{ie_version} >= 5.5 or $brws{mozilla_version} >= 1.4) ? return 1 : return 0; } else { return \%brws; } } sub load_config { # -------------------------------------------------------------------- # Load the config file into a hash. # my $self = shift; my $file = $self->{cfg_path} || 'ConfigData.pm'; my $cfg = do $file; if (ref $cfg ne 'HASH') { die "Invalid config file: $file. Got: '$cfg' instead of actual data. Error: $@ $!"; } return $cfg; } sub fatal { # -------------------------------------------------------------- # Return a fatal error message to the browser. # die @_ if (GT::Base->in_eval()); # Don't do anything if we are in eval. my $msg = shift; my $in = new GT::CGI; print $in->header; my $work_path = $in->param('work_path') || ''; print qq! A fatal error has occured:
$msg
Please enable debugging in setup for more details.
\n !; if ($DEBUG) { print base_env(); } } sub base_env { # -------------------------------------------------------------------- # Return HTML formatted environment for error messages. # my $info = ''; # Stack trace. my $i = 0; $info .= "Stack Trace\n======================================\n"; $info .= GT::Base::stack_trace('FileMan', 1, 1); $info .= "\n\n"; $info .= "System Information\n======================================\n"; $info .= "Perl Version: $]\n"; $info .= "FileMan Version: $FileMan::VERSION\n" if ($FileMan::VERSION); $info .= "Persistant Env: mod_perl (" . (MOD_PERL ? 1 : 0) . ") SpeedyCGI (" . (SPEEDY ? 1 : 0) . ")\n"; $info .= "Mod Perl Version: " . MOD_PERL . "\n" if MOD_PERL; $info .= "\@INC = \n\t" . join ("\n\t", @INC) . "\n"; $info .= "\$\@: $@\n" if ($@); $info .= "\n"; # Environment info. $info .= "ENVIRONMENT\n======================================\n"; foreach (sort keys %ENV) { $info .= "$_ => $ENV{$_}\n"; } $info .= ""; return $info; } sub js_quote_include { # -------------------------------------------------------------------- # This uses GT::Template to parse the passed in argument. The results are # javascript escaped, and then returned. # my $file = shift; my $tags = GT::Template->tags; my $in = new GT::CGI; my $css_file = $in->cookie('scheme') || 'fileman'; my $color; CASE: { ($css_file eq 'fileman') and $color = '#D6D6D6', last CASE; ($css_file eq 'gt') and $color = '#d9e4f2', last CASE; ($css_file eq 'maple') and $color = '#F0E8CE', last CASE; ($css_file eq 'rainy') and $color = '#CFD8C2', last CASE; ($css_file eq 'rose') and $color = '#DEC9CE', last CASE; } my $parsed = GT::Template->parse("$tags->{template_root}/common/$file", { html_url => $tags->{html_url}, http_ref => $tags->{http_ref}, filename => $tags->{filename}, work_path => $tags->{work_path}, scrollbar_arrow_color => 'black', scrollbar_base_color => $color, editor_base_color => $color, advanced_editor_background => 'white', advanced_editor_font => 'arial' }); $parsed =~ s{([\\/'"<>])}{\\$1}g; $parsed =~ s/(?:\r\n|\r|\n)/\\n/g; return \$parsed; } 1;